From 37a23b39624325599a7555ba3ab8fa9a27a45333 Mon Sep 17 00:00:00 2001 From: stslex Date: Thu, 26 Jun 2025 20:11:55 +0300 Subject: [PATCH 1/8] update libs --- build-logic/convention/build.gradle.kts | 9 ++++- .../kotlin/KMPApplicationConventionPlugin.kt | 25 ++++---------- .../KMPLibraryComposeConventionPlugin.kt | 18 +++------- .../main/kotlin/KMPLibraryConventionPlugin.kt | 15 +++----- .../main/kotlin/KotlinLibraryComposePlugin.kt | 5 +-- .../kotlin/RoomLibraryConventionPlugin.kt | 5 ++- .../com/stslex/atten/convention/KmpCompose.kt | 23 +++++++------ .../stslex/atten/convention/KotlinAndroid.kt | 4 +-- .../atten/convention/KotlinAndroidCompose.kt | 2 -- .../atten/convention/KotlinConfigure.kt | 6 ++-- .../atten/convention/KotlinMultiplatform.kt | 7 +--- .../stslex/atten/convention/KspConfigure.kt | 17 ++++++++++ build-logic/settings.gradle.kts | 7 ++++ commonApp/build.gradle.kts | 4 +-- .../commonMain/kotlin/com/stslex/atten/App.kt | 2 +- .../kotlin/com/stslex/atten/di/AppModules.kt | 2 +- .../kotlin/com/stslex/atten/ui/InitialApp.kt | 2 +- .../navigator/Navigator.kt | 14 -------- core/ui/{ => kit}/build.gradle.kts | 0 .../core/ui/kit}/mvi/StoreExt.android.kt | 2 +- .../core/ui/kit}/theme/AppTheme.android.kt | 2 +- .../core/ui/kit}/components/AppToolbar.kt | 4 +-- .../ui/kit}/components/CardAnimateBorder.kt | 4 +-- .../kit}/components/snackbar/AppSnackbar.kt | 6 ++-- .../components/snackbar/SnackbarSwipeState.kt | 2 +- .../kit}/components/snackbar/SnackbarType.kt | 2 +- .../stslex/atten/core/ui/kit}/mvi/Router.kt | 2 +- .../stslex/atten/core/ui/kit}/mvi/Store.kt | 10 +++--- .../core/ui/kit}/mvi/StoreAbstraction.kt | 8 ++--- .../atten/core/ui/kit}/mvi/StoreComponent.kt | 4 +-- .../stslex/atten/core/ui/kit}/mvi/StoreExt.kt | 2 +- .../atten/core/ui/kit}/theme/AppColors.kt | 2 +- .../atten/core/ui/kit}/theme/AppDimension.kt | 2 +- .../atten/core/ui/kit}/theme/AppTheme.kt | 2 +- .../atten/core/ui/kit}/theme/DimenExt.kt | 2 +- .../kit}/mvi/StoreExt.ios.kt | 2 +- .../kit}/theme/AppTheme.ios.kt | 2 +- core/ui/mvi/build.gradle.kts | 9 +++++ core/{ => ui}/navigation/build.gradle.kts | 0 .../atten/core/ui/navigation}/NavExt.kt | 2 +- .../atten/core/ui/navigation/Navigator.kt | 11 ++++++ .../core/ui/navigation}/NavigatorImpl.kt | 4 +-- .../core/ui/navigation}/NavigatorOptions.kt | 2 +- .../atten/core/ui/navigation}/Screen.kt | 2 +- .../ui/navigation}/di/ModuleCoreNavigation.kt | 6 ++-- feature/details/build.gradle.kts | 4 +-- .../di/FeatureDetailsModule.kt | 2 +- .../navigation/DetailsGraph.kt | 6 ++-- .../navigation/DetailsRouter.kt | 4 +-- .../ui/DetailsScreen.kt | 4 +-- .../ui/store/DetailsStore.kt | 4 +-- .../ui/store/DetailsStoreComponent.kt | 2 +- feature/home/build.gradle.kts | 4 +-- .../di/ModuleFeatureHome.kt | 2 +- .../navigation/HomeGraph.kt | 6 ++-- .../navigation/HomeRouter.kt | 2 +- .../navigation/HomeRouterImpl.kt | 4 +-- .../ui/HomeScreen.kt | 10 +++--- .../ui/components/HomeScreenItem.kt | 6 ++-- .../ui/store/HomeStore.kt | 4 +-- .../ui/store/HomeStoreComponent.kt | 2 +- gradle.properties | 3 +- gradle/libs.versions.toml | 34 +++++++++---------- gradle/wrapper/gradle-wrapper.properties | 6 ++-- settings.gradle.kts | 5 +-- 65 files changed, 189 insertions(+), 188 deletions(-) create mode 100644 build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KspConfigure.kt delete mode 100644 core/navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/navigator/Navigator.kt rename core/ui/{ => kit}/build.gradle.kts (100%) rename core/ui/{src/androidMain/kotlin/com.stslex.atten.core.ui => kit/src/androidMain/kotlin/com/stslex/atten/core/ui/kit}/mvi/StoreExt.android.kt (95%) rename core/ui/{src/androidMain/kotlin/com.stslex.atten.core.ui => kit/src/androidMain/kotlin/com/stslex/atten/core/ui/kit}/theme/AppTheme.android.kt (94%) rename core/ui/{src/commonMain/kotlin/com.stslex.atten.core.ui => kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit}/components/AppToolbar.kt (95%) rename core/ui/{src/commonMain/kotlin/com.stslex.atten.core.ui => kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit}/components/CardAnimateBorder.kt (98%) rename core/ui/{src/commonMain/kotlin/com.stslex.atten.core.ui => kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit}/components/snackbar/AppSnackbar.kt (97%) rename core/ui/{src/commonMain/kotlin/com.stslex.atten.core.ui => kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit}/components/snackbar/SnackbarSwipeState.kt (57%) rename core/ui/{src/commonMain/kotlin/com.stslex.atten.core.ui => kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit}/components/snackbar/SnackbarType.kt (94%) rename core/ui/{src/commonMain/kotlin/com.stslex.atten.core.ui => kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit}/mvi/Router.kt (69%) rename core/ui/{src/commonMain/kotlin/com.stslex.atten.core.ui => kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit}/mvi/Store.kt (93%) rename core/ui/{src/commonMain/kotlin/com.stslex.atten.core.ui => kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit}/mvi/StoreAbstraction.kt (73%) rename core/ui/{src/commonMain/kotlin/com.stslex.atten.core.ui => kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit}/mvi/StoreComponent.kt (94%) rename core/ui/{src/commonMain/kotlin/com.stslex.atten.core.ui => kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit}/mvi/StoreExt.kt (98%) rename core/ui/{src/commonMain/kotlin/com.stslex.atten.core.ui => kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit}/theme/AppColors.kt (75%) rename core/ui/{src/commonMain/kotlin/com.stslex.atten.core.ui => kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit}/theme/AppDimension.kt (93%) rename core/ui/{src/commonMain/kotlin/com.stslex.atten.core.ui => kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit}/theme/AppTheme.kt (93%) rename core/ui/{src/commonMain/kotlin/com.stslex.atten.core.ui => kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit}/theme/DimenExt.kt (93%) rename core/ui/{src/iosMain/kotlin/com.stslex.atten.core.ui => kit/src/iosMain/kotlin/com.stslex.atten.core.ui/kit}/mvi/StoreExt.ios.kt (94%) rename core/ui/{src/iosMain/kotlin/com.stslex.atten.core.ui => kit/src/iosMain/kotlin/com.stslex.atten.core.ui/kit}/theme/AppTheme.ios.kt (89%) create mode 100644 core/ui/mvi/build.gradle.kts rename core/{ => ui}/navigation/build.gradle.kts (100%) rename core/{navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation => ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation}/NavExt.kt (89%) create mode 100644 core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Navigator.kt rename core/{navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/navigator => ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation}/NavigatorImpl.kt (86%) rename core/{navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation => ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation}/NavigatorOptions.kt (61%) rename core/{navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation => ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation}/Screen.kt (86%) rename core/{navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation => ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation}/di/ModuleCoreNavigation.kt (70%) diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index a124bca..e26f8b2 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -8,10 +8,17 @@ dependencies { compileOnly(libs.android.gradlePlugin) //if targetting Android compileOnly(libs.kotlin.gradlePlugin) compileOnly(libs.compose.gradlePlugin) //if you are using Compose Multiplatform - compileOnly(libs.ksp.gradlePlugin) + implementation(libs.ksp.gradlePlugin) compileOnly(libs.room.gradlePlugin) //if you are using Room } +tasks { + validatePlugins { + enableStricterValidation = true + failOnWarning = true + } +} + gradlePlugin { plugins { register("kotlinLibraryMultiplatform") { diff --git a/build-logic/convention/src/main/kotlin/KMPApplicationConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KMPApplicationConventionPlugin.kt index afb1114..5533ede 100644 --- a/build-logic/convention/src/main/kotlin/KMPApplicationConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/KMPApplicationConventionPlugin.kt @@ -4,19 +4,15 @@ import AppExt.findVersionInt import AppExt.findVersionString import AppExt.libs import com.android.build.api.dsl.ApplicationExtension -import com.android.build.api.dsl.CommonExtension -import com.google.devtools.ksp.gradle.KspExtension import com.stslex.atten.convention.configureKMPCompose import com.stslex.atten.convention.configureKotlin import com.stslex.atten.convention.configureKotlinAndroid import com.stslex.atten.convention.configureKotlinAndroidCompose import com.stslex.atten.convention.configureKotlinMultiplatform +import com.stslex.atten.convention.configureKsp import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure -import org.gradle.kotlin.dsl.getByType -import org.jetbrains.compose.ComposeExtension -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension class KMPApplicationConventionPlugin : Plugin { @@ -27,25 +23,16 @@ class KMPApplicationConventionPlugin : Plugin { apply(libs.findPluginId("androidApplication")) apply(libs.findPluginId("jetbrainsCompose")) apply(libs.findPluginId("composeCompiler")) - apply(libs.findPluginId("ksp")) apply(libs.findPluginId("serialization")) } - extensions.configure { - val kspExtension = extensions.getByType() - configureKotlinMultiplatform(this, kspExtension) - configureKMPCompose( - extension = this, - compose = extensions.getByType().dependencies - ) - } + configureKsp() + configureKotlinMultiplatform() + configureKMPCompose() + configureKotlin() extensions.configure { - configureKotlin() - configureKotlinAndroid( - extension = this, - isApp = true - ) + configureKotlinAndroid(this) configureKotlinAndroidCompose(this) defaultConfig.apply { applicationId = APP_PREFIX diff --git a/build-logic/convention/src/main/kotlin/KMPLibraryComposeConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KMPLibraryComposeConventionPlugin.kt index b903623..261faaf 100644 --- a/build-logic/convention/src/main/kotlin/KMPLibraryComposeConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/KMPLibraryComposeConventionPlugin.kt @@ -5,12 +5,10 @@ import com.stslex.atten.convention.configureKMPCompose import com.stslex.atten.convention.configureKotlin import com.stslex.atten.convention.configureKotlinAndroid import com.stslex.atten.convention.configureKotlinMultiplatform +import com.stslex.atten.convention.configureKsp import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure -import org.gradle.kotlin.dsl.getByType -import org.jetbrains.compose.ComposeExtension -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension class KMPLibraryComposeConventionPlugin : Plugin { @@ -21,20 +19,12 @@ class KMPLibraryComposeConventionPlugin : Plugin { apply(libs.findPluginId("composeCompiler")) apply(libs.findPluginId("kotlinCocoapods")) apply(libs.findPluginId("androidLibrary")) - apply(libs.findPluginId("ksp")) apply(libs.findPluginId("serialization")) } - extensions.configure { - configureKMPCompose( - extension = this, - compose = extensions.getByType().dependencies - ) - configureKotlinMultiplatform( - extension = this, - kspExtension = extensions.getByType() - ) - } + configureKsp() + configureKMPCompose() + configureKotlinMultiplatform() extensions.configure { configureKotlin() diff --git a/build-logic/convention/src/main/kotlin/KMPLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KMPLibraryConventionPlugin.kt index 2b04de3..a516011 100644 --- a/build-logic/convention/src/main/kotlin/KMPLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/KMPLibraryConventionPlugin.kt @@ -4,11 +4,10 @@ import com.android.build.api.dsl.LibraryExtension import com.stslex.atten.convention.configureKotlin import com.stslex.atten.convention.configureKotlinAndroid import com.stslex.atten.convention.configureKotlinMultiplatform +import com.stslex.atten.convention.configureKsp import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure -import org.gradle.kotlin.dsl.getByType -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension class KMPLibraryConventionPlugin : Plugin { @@ -17,19 +16,15 @@ class KMPLibraryConventionPlugin : Plugin { apply(libs.findPluginId("kotlinMultiplatform")) apply(libs.findPluginId("kotlinCocoapods")) apply(libs.findPluginId("androidLibrary")) - apply(libs.findPluginId("ksp")) apply(libs.findPluginId("serialization")) } - extensions.configure { - configureKotlinMultiplatform( - extension = this, - kspExtension = extensions.getByType() - ) - } + configureKsp() + configureKotlinMultiplatform() + configureKotlin() + extensions.configure { configureKotlinAndroid(this) - configureKotlin() } } } \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/KotlinLibraryComposePlugin.kt b/build-logic/convention/src/main/kotlin/KotlinLibraryComposePlugin.kt index f2458c8..aeecdd3 100644 --- a/build-logic/convention/src/main/kotlin/KotlinLibraryComposePlugin.kt +++ b/build-logic/convention/src/main/kotlin/KotlinLibraryComposePlugin.kt @@ -3,6 +3,7 @@ import AppExt.libs import com.android.build.api.dsl.LibraryExtension import com.stslex.atten.convention.configureKotlin import com.stslex.atten.convention.configureKotlinAndroidCompose +import com.stslex.atten.convention.configureKsp import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure @@ -13,10 +14,10 @@ class KotlinLibraryComposePlugin : Plugin { with(pluginManager) { apply(libs.findPluginId("androidLibrary")) apply(libs.findPluginId("composeCompiler")) - apply(libs.findPluginId("ksp")) } + configureKsp() + configureKotlin() extensions.configure { - configureKotlin() configureKotlinAndroidCompose(this) } } diff --git a/build-logic/convention/src/main/kotlin/RoomLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/RoomLibraryConventionPlugin.kt index e582a6a..9444ba0 100644 --- a/build-logic/convention/src/main/kotlin/RoomLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/RoomLibraryConventionPlugin.kt @@ -1,7 +1,7 @@ import AppExt.findPluginId import AppExt.libs import androidx.room.gradle.RoomExtension -import com.google.devtools.ksp.gradle.KspExtension +import com.stslex.atten.convention.configureKsp import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.kotlin.dsl.configure @@ -14,12 +14,11 @@ class RoomLibraryConventionPlugin : Plugin { with(target) { pluginManager.apply { apply(libs.findPluginId("room")) - apply(libs.findPluginId("ksp")) apply(libs.findPluginId("kotlinMultiplatform")) apply(libs.findPluginId("serialization")) } - extensions.configure { + configureKsp { arg("room.generateKotlin", "true") } diff --git a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KmpCompose.kt b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KmpCompose.kt index 805e58d..e11d05b 100644 --- a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KmpCompose.kt +++ b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KmpCompose.kt @@ -2,22 +2,23 @@ package com.stslex.atten.convention import AppExt.libs import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.getByType import org.jetbrains.compose.ComposePlugin import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension -fun Project.configureKMPCompose( - extension: KotlinMultiplatformExtension, - compose: ComposePlugin.Dependencies -) = extension.apply { +fun Project.configureKMPCompose() = extensions.configure { + val dependencies = extensions.getByType() sourceSets.apply { commonMain.dependencies { - // todo need to add compose dependencies - implementation(compose.ui) - implementation(compose.material3) - implementation(compose.foundation) - implementation(compose.components.uiToolingPreview) - implementation(compose.components.resources) - implementation(compose.runtime) + implementation(dependencies.ui) + implementation(dependencies.material3) + implementation(dependencies.foundation) + implementation(dependencies.components.uiToolingPreview) + implementation(dependencies.components.resources) + implementation(dependencies.runtime) + implementation(dependencies.materialIconsExtended) + implementation(libs.findLibrary("kotlinx-collections-immutable").get()) implementation(libs.findLibrary("koin-compose").get()) implementation(libs.findLibrary("lifecycle-viewmodel").get()) diff --git a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinAndroid.kt b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinAndroid.kt index 8787d13..d630b4c 100644 --- a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinAndroid.kt +++ b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinAndroid.kt @@ -3,6 +3,7 @@ package com.stslex.atten.convention import AppExt.APP_PREFIX import AppExt.findVersionInt import AppExt.libs +import com.android.build.api.dsl.ApplicationExtension import com.android.build.api.dsl.CommonExtension import org.gradle.api.JavaVersion import org.gradle.api.Project @@ -10,11 +11,10 @@ import org.gradle.kotlin.dsl.dependencies internal fun Project.configureKotlinAndroid( extension: CommonExtension<*, *, *, *, *, *>, - isApp: Boolean = false ) = extension.apply { //get module name from module path - val dropValue = if (isApp) 2 else 1 + val dropValue = if (extension is ApplicationExtension) 2 else 1 val moduleName = path.split(":") .drop(dropValue) .joinToString(".") diff --git a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinAndroidCompose.kt b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinAndroidCompose.kt index 6e2289d..1b2838e 100644 --- a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinAndroidCompose.kt +++ b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinAndroidCompose.kt @@ -1,6 +1,5 @@ package com.stslex.atten.convention -import AppExt.findVersionString import AppExt.libs import com.android.build.api.dsl.CommonExtension import org.gradle.api.Project @@ -10,7 +9,6 @@ fun Project.configureKotlinAndroidCompose( commonExtension: CommonExtension<*, *, *, *, *, *>, ) = commonExtension.apply { buildFeatures.compose = true - composeOptions.kotlinCompilerExtensionVersion = libs.findVersionString("compose-compiler") dependencies { "implementation"(libs.findLibrary("compose.ui").get()) diff --git a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinConfigure.kt b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinConfigure.kt index cf3cad6..2916553 100644 --- a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinConfigure.kt +++ b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinConfigure.kt @@ -6,9 +6,9 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompileCommon fun Project.configureKotlin() { tasks.withType().configureEach { - @Suppress("Deprecation") - kotlinOptions { - freeCompilerArgs = freeCompilerArgs + "-Xexpect-actual-classes" + @Suppress("MISSING_DEPENDENCY_SUPERCLASS_IN_TYPE_ARGUMENT") + compilerOptions { + freeCompilerArgs.addAll("-Xexpect-actual-classes") } } } \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinMultiplatform.kt b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinMultiplatform.kt index a16a8a8..879b0e4 100644 --- a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinMultiplatform.kt +++ b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinMultiplatform.kt @@ -1,7 +1,6 @@ package com.stslex.atten.convention import AppExt.libs -import com.google.devtools.ksp.gradle.KspExtension import org.gradle.api.Project import org.gradle.api.plugins.ExtensionAware import org.gradle.kotlin.dsl.configure @@ -10,11 +9,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension internal fun Project.configureKotlinMultiplatform( - extension: KotlinMultiplatformExtension, - kspExtension: KspExtension -) = extension.apply { - kspExtension.arg("KOIN_CONFIG_CHECK", "true") - +) = extensions.configure { jvmToolchain(17) // targets diff --git a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KspConfigure.kt b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KspConfigure.kt new file mode 100644 index 0000000..987b7d0 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KspConfigure.kt @@ -0,0 +1,17 @@ +package com.stslex.atten.convention + +import AppExt.findPluginId +import AppExt.libs +import com.google.devtools.ksp.gradle.KspExtension +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +fun Project.configureKsp( + extraBlock: KspExtension.() -> Unit = {} +) { + pluginManager.apply(libs.findPluginId("ksp")) + extensions.configure { + arg("KOIN_CONFIG_CHECK", "true") + extraBlock() + } +} \ No newline at end of file diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts index a249d72..ebb08b0 100644 --- a/build-logic/settings.gradle.kts +++ b/build-logic/settings.gradle.kts @@ -1,5 +1,12 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") +pluginManagement { + repositories { + gradlePluginPortal() + google() + } +} + @Suppress("UnstableApiUsage") dependencyResolutionManagement { repositories { diff --git a/commonApp/build.gradle.kts b/commonApp/build.gradle.kts index 2a4091d..5a3c5a2 100644 --- a/commonApp/build.gradle.kts +++ b/commonApp/build.gradle.kts @@ -7,9 +7,9 @@ kotlin { commonMain { dependencies { implementation(project(":core:core")) - implementation(project(":core:ui")) + implementation(project(":core:ui:kit")) + implementation(project(":core:ui:navigation")) implementation(project(":core:database")) - implementation(project(":core:navigation")) implementation(project(":core:paging")) implementation(project(":core:todo")) diff --git a/commonApp/src/commonMain/kotlin/com/stslex/atten/App.kt b/commonApp/src/commonMain/kotlin/com/stslex/atten/App.kt index 2d5cf5a..1653fdf 100644 --- a/commonApp/src/commonMain/kotlin/com/stslex/atten/App.kt +++ b/commonApp/src/commonMain/kotlin/com/stslex/atten/App.kt @@ -2,7 +2,7 @@ package com.stslex.atten import androidx.compose.runtime.Composable import com.stslex.atten.config.KoinApp -import com.stslex.atten.core.ui.theme.AppTheme +import com.stslex.atten.core.ui.kit.theme.AppTheme import com.stslex.atten.ui.InitialApp import org.koin.core.KoinApplication diff --git a/commonApp/src/commonMain/kotlin/com/stslex/atten/di/AppModules.kt b/commonApp/src/commonMain/kotlin/com/stslex/atten/di/AppModules.kt index 93d438a..4390468 100644 --- a/commonApp/src/commonMain/kotlin/com/stslex/atten/di/AppModules.kt +++ b/commonApp/src/commonMain/kotlin/com/stslex/atten/di/AppModules.kt @@ -3,7 +3,7 @@ package com.stslex.atten.di import androidx.navigation.NavHostController import com.stslex.atten.core.database.di.ModuleCoreDatabase import com.stslex.atten.core.di.ModuleCore -import com.stslex.atten.core.navigation.di.ModuleCoreNavigation +import com.stslex.atten.core.ui.navigation.di.ModuleCoreNavigation import com.stslex.atten.core.paging.di.ModuleCorePaging import com.stslex.atten.core.todo.di.ModuleCoreToDo import com.stslex.atten.feature.details.di.FeatureDetailsModule diff --git a/commonApp/src/commonMain/kotlin/com/stslex/atten/ui/InitialApp.kt b/commonApp/src/commonMain/kotlin/com/stslex/atten/ui/InitialApp.kt index bea4ea3..bd513e5 100644 --- a/commonApp/src/commonMain/kotlin/com/stslex/atten/ui/InitialApp.kt +++ b/commonApp/src/commonMain/kotlin/com/stslex/atten/ui/InitialApp.kt @@ -6,7 +6,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost -import com.stslex.atten.core.navigation.Screen +import com.stslex.atten.core.ui.navigation.Screen import com.stslex.atten.feature.details.navigation.detailsGraph import com.stslex.atten.feature.home.navigation.homeGraph diff --git a/core/navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/navigator/Navigator.kt b/core/navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/navigator/Navigator.kt deleted file mode 100644 index 41e20e6..0000000 --- a/core/navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/navigator/Navigator.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.stslex.atten.core.navigation.navigator - -import com.stslex.atten.core.navigation.NavigatorOptions -import com.stslex.atten.core.navigation.Screen - -interface Navigator { - - fun navigateTo( - screen: Screen, - options: NavigatorOptions = NavigatorOptions() - ) - - fun popBack() -} diff --git a/core/ui/build.gradle.kts b/core/ui/kit/build.gradle.kts similarity index 100% rename from core/ui/build.gradle.kts rename to core/ui/kit/build.gradle.kts diff --git a/core/ui/src/androidMain/kotlin/com.stslex.atten.core.ui/mvi/StoreExt.android.kt b/core/ui/kit/src/androidMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreExt.android.kt similarity index 95% rename from core/ui/src/androidMain/kotlin/com.stslex.atten.core.ui/mvi/StoreExt.android.kt rename to core/ui/kit/src/androidMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreExt.android.kt index b89229b..75512b5 100644 --- a/core/ui/src/androidMain/kotlin/com.stslex.atten.core.ui/mvi/StoreExt.android.kt +++ b/core/ui/kit/src/androidMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreExt.android.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.ui.mvi +package com.stslex.atten.core.ui.kit.mvi import androidx.compose.runtime.Composable import androidx.lifecycle.ViewModel diff --git a/core/ui/src/androidMain/kotlin/com.stslex.atten.core.ui/theme/AppTheme.android.kt b/core/ui/kit/src/androidMain/kotlin/com/stslex/atten/core/ui/kit/theme/AppTheme.android.kt similarity index 94% rename from core/ui/src/androidMain/kotlin/com.stslex.atten.core.ui/theme/AppTheme.android.kt rename to core/ui/kit/src/androidMain/kotlin/com/stslex/atten/core/ui/kit/theme/AppTheme.android.kt index 8b5b97f..62551a5 100644 --- a/core/ui/src/androidMain/kotlin/com.stslex.atten.core.ui/theme/AppTheme.android.kt +++ b/core/ui/kit/src/androidMain/kotlin/com/stslex/atten/core/ui/kit/theme/AppTheme.android.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.ui.theme +package com.stslex.atten.core.ui.kit.theme import android.os.Build import androidx.compose.material3.ColorScheme diff --git a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/components/AppToolbar.kt b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/components/AppToolbar.kt similarity index 95% rename from core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/components/AppToolbar.kt rename to core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/components/AppToolbar.kt index 62d9a48..cde008f 100644 --- a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/components/AppToolbar.kt +++ b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/components/AppToolbar.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.ui.components +package com.stslex.atten.core.ui.kit.components import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -15,7 +15,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow -import com.stslex.atten.core.ui.theme.AppDimension +import com.stslex.atten.core.ui.kit.theme.AppDimension @Composable fun AppToolbar( diff --git a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/components/CardAnimateBorder.kt b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/components/CardAnimateBorder.kt similarity index 98% rename from core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/components/CardAnimateBorder.kt rename to core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/components/CardAnimateBorder.kt index 948e2c5..3e2c283 100644 --- a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/components/CardAnimateBorder.kt +++ b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/components/CardAnimateBorder.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.ui.components +package com.stslex.atten.core.ui.kit.components import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.RepeatMode @@ -27,7 +27,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.drawscope.rotate import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.stslex.atten.core.ui.theme.AppDimension +import com.stslex.atten.core.ui.kit.theme.AppDimension @OptIn(ExperimentalFoundationApi::class) @Composable diff --git a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/components/snackbar/AppSnackbar.kt b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/components/snackbar/AppSnackbar.kt similarity index 97% rename from core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/components/snackbar/AppSnackbar.kt rename to core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/components/snackbar/AppSnackbar.kt index 33c6001..74fe660 100644 --- a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/components/snackbar/AppSnackbar.kt +++ b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/components/snackbar/AppSnackbar.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.ui.components.snackbar +package com.stslex.atten.core.ui.kit.components.snackbar import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.BoxScope @@ -27,8 +27,8 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset -import com.stslex.atten.core.ui.theme.AppDimension -import com.stslex.atten.core.ui.theme.toPx +import com.stslex.atten.core.ui.kit.theme.AppDimension +import com.stslex.atten.core.ui.kit.theme.toPx import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlin.math.roundToInt diff --git a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/components/snackbar/SnackbarSwipeState.kt b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/components/snackbar/SnackbarSwipeState.kt similarity index 57% rename from core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/components/snackbar/SnackbarSwipeState.kt rename to core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/components/snackbar/SnackbarSwipeState.kt index 8bc092b..601263a 100644 --- a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/components/snackbar/SnackbarSwipeState.kt +++ b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/components/snackbar/SnackbarSwipeState.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.ui.components.snackbar +package com.stslex.atten.core.ui.kit.components.snackbar internal enum class SnackbarSwipeState { LEFT, diff --git a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/components/snackbar/SnackbarType.kt b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/components/snackbar/SnackbarType.kt similarity index 94% rename from core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/components/snackbar/SnackbarType.kt rename to core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/components/snackbar/SnackbarType.kt index 3d460e0..83d8a67 100644 --- a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/components/snackbar/SnackbarType.kt +++ b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/components/snackbar/SnackbarType.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.ui.components.snackbar +package com.stslex.atten.core.ui.kit.components.snackbar import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Done diff --git a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/mvi/Router.kt b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/Router.kt similarity index 69% rename from core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/mvi/Router.kt rename to core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/Router.kt index 00e90a9..3bd46b9 100644 --- a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/mvi/Router.kt +++ b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/Router.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.ui.mvi +package com.stslex.atten.core.ui.kit.mvi fun interface Router { operator fun invoke(event: E) diff --git a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/mvi/Store.kt b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/Store.kt similarity index 93% rename from core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/mvi/Store.kt rename to core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/Store.kt index 35edf68..b5ff7b5 100644 --- a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/mvi/Store.kt +++ b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/Store.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.ui.mvi +package com.stslex.atten.core.ui.kit.mvi import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -6,10 +6,10 @@ import com.stslex.atten.core.Logger import com.stslex.atten.core.coroutine.dispatcher.AppDispatcher import com.stslex.atten.core.coroutine.scope.AppCoroutineScope import com.stslex.atten.core.coroutine.scope.AppCoroutineScopeImpl -import com.stslex.atten.core.ui.mvi.StoreComponent.Action -import com.stslex.atten.core.ui.mvi.StoreComponent.Event -import com.stslex.atten.core.ui.mvi.StoreComponent.Navigation -import com.stslex.atten.core.ui.mvi.StoreComponent.State +import com.stslex.atten.core.ui.kit.mvi.StoreComponent.Action +import com.stslex.atten.core.ui.kit.mvi.StoreComponent.Event +import com.stslex.atten.core.ui.kit.mvi.StoreComponent.Navigation +import com.stslex.atten.core.ui.kit.mvi.StoreComponent.State import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow diff --git a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/mvi/StoreAbstraction.kt b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreAbstraction.kt similarity index 73% rename from core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/mvi/StoreAbstraction.kt rename to core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreAbstraction.kt index 38b893f..ad547cd 100644 --- a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/mvi/StoreAbstraction.kt +++ b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreAbstraction.kt @@ -1,8 +1,8 @@ -package com.stslex.atten.core.ui.mvi +package com.stslex.atten.core.ui.kit.mvi -import com.stslex.atten.core.ui.mvi.StoreComponent.Action -import com.stslex.atten.core.ui.mvi.StoreComponent.Event -import com.stslex.atten.core.ui.mvi.StoreComponent.State +import com.stslex.atten.core.ui.kit.mvi.StoreComponent.Action +import com.stslex.atten.core.ui.kit.mvi.StoreComponent.Event +import com.stslex.atten.core.ui.kit.mvi.StoreComponent.State import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow diff --git a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/mvi/StoreComponent.kt b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreComponent.kt similarity index 94% rename from core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/mvi/StoreComponent.kt rename to core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreComponent.kt index 180a14d..9ae6379 100644 --- a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/mvi/StoreComponent.kt +++ b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreComponent.kt @@ -1,8 +1,8 @@ -package com.stslex.atten.core.ui.mvi +package com.stslex.atten.core.ui.kit.mvi import androidx.compose.material3.SnackbarDuration import androidx.compose.runtime.Stable -import com.stslex.atten.core.ui.components.snackbar.SnackbarType +import com.stslex.atten.core.ui.kit.components.snackbar.SnackbarType interface StoreComponent { diff --git a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/mvi/StoreExt.kt b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreExt.kt similarity index 98% rename from core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/mvi/StoreExt.kt rename to core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreExt.kt index 30aa466..917a9f7 100644 --- a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/mvi/StoreExt.kt +++ b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreExt.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.ui.mvi +package com.stslex.atten.core.ui.kit.mvi import androidx.compose.runtime.Composable import androidx.lifecycle.ViewModel diff --git a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/theme/AppColors.kt b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/theme/AppColors.kt similarity index 75% rename from core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/theme/AppColors.kt rename to core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/theme/AppColors.kt index 845d9dd..9ea9d60 100644 --- a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/theme/AppColors.kt +++ b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/theme/AppColors.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.ui.theme +package com.stslex.atten.core.ui.kit.theme import androidx.compose.ui.graphics.Color diff --git a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/theme/AppDimension.kt b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/theme/AppDimension.kt similarity index 93% rename from core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/theme/AppDimension.kt rename to core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/theme/AppDimension.kt index 39cd14e..1c83e4b 100644 --- a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/theme/AppDimension.kt +++ b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/theme/AppDimension.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.ui.theme +package com.stslex.atten.core.ui.kit.theme import androidx.compose.ui.unit.dp diff --git a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/theme/AppTheme.kt b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/theme/AppTheme.kt similarity index 93% rename from core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/theme/AppTheme.kt rename to core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/theme/AppTheme.kt index 222c74d..d1c286b 100644 --- a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/theme/AppTheme.kt +++ b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/theme/AppTheme.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.ui.theme +package com.stslex.atten.core.ui.kit.theme import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.ColorScheme diff --git a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/theme/DimenExt.kt b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/theme/DimenExt.kt similarity index 93% rename from core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/theme/DimenExt.kt rename to core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/theme/DimenExt.kt index 3ebbcb4..165135c 100644 --- a/core/ui/src/commonMain/kotlin/com.stslex.atten.core.ui/theme/DimenExt.kt +++ b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/theme/DimenExt.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.ui.theme +package com.stslex.atten.core.ui.kit.theme import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable diff --git a/core/ui/src/iosMain/kotlin/com.stslex.atten.core.ui/mvi/StoreExt.ios.kt b/core/ui/kit/src/iosMain/kotlin/com.stslex.atten.core.ui/kit/mvi/StoreExt.ios.kt similarity index 94% rename from core/ui/src/iosMain/kotlin/com.stslex.atten.core.ui/mvi/StoreExt.ios.kt rename to core/ui/kit/src/iosMain/kotlin/com.stslex.atten.core.ui/kit/mvi/StoreExt.ios.kt index b3b8c64..bd79146 100644 --- a/core/ui/src/iosMain/kotlin/com.stslex.atten.core.ui/mvi/StoreExt.ios.kt +++ b/core/ui/kit/src/iosMain/kotlin/com.stslex.atten.core.ui/kit/mvi/StoreExt.ios.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.ui.mvi +package com.stslex.atten.core.ui.kit.mvi import androidx.compose.runtime.Composable import androidx.lifecycle.ViewModel diff --git a/core/ui/src/iosMain/kotlin/com.stslex.atten.core.ui/theme/AppTheme.ios.kt b/core/ui/kit/src/iosMain/kotlin/com.stslex.atten.core.ui/kit/theme/AppTheme.ios.kt similarity index 89% rename from core/ui/src/iosMain/kotlin/com.stslex.atten.core.ui/theme/AppTheme.ios.kt rename to core/ui/kit/src/iosMain/kotlin/com.stslex.atten.core.ui/kit/theme/AppTheme.ios.kt index af4ebbd..dc77fdf 100644 --- a/core/ui/src/iosMain/kotlin/com.stslex.atten.core.ui/theme/AppTheme.ios.kt +++ b/core/ui/kit/src/iosMain/kotlin/com.stslex.atten.core.ui/kit/theme/AppTheme.ios.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.ui.theme +package com.stslex.atten.core.ui.kit.theme import androidx.compose.material3.ColorScheme import androidx.compose.material3.darkColorScheme diff --git a/core/ui/mvi/build.gradle.kts b/core/ui/mvi/build.gradle.kts new file mode 100644 index 0000000..3e72290 --- /dev/null +++ b/core/ui/mvi/build.gradle.kts @@ -0,0 +1,9 @@ +plugins { + alias(libs.plugins.convention.kmp.library.compose) +} + +kotlin { + sourceSets.commonMain.dependencies { + implementation(project(":core:core")) + } +} \ No newline at end of file diff --git a/core/navigation/build.gradle.kts b/core/ui/navigation/build.gradle.kts similarity index 100% rename from core/navigation/build.gradle.kts rename to core/ui/navigation/build.gradle.kts diff --git a/core/navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/NavExt.kt b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavExt.kt similarity index 89% rename from core/navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/NavExt.kt rename to core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavExt.kt index 05a5263..17667e0 100644 --- a/core/navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/NavExt.kt +++ b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavExt.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.navigation +package com.stslex.atten.core.ui.navigation import androidx.compose.runtime.Composable import androidx.navigation.NavGraphBuilder diff --git a/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Navigator.kt b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Navigator.kt new file mode 100644 index 0000000..bd8b8ea --- /dev/null +++ b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Navigator.kt @@ -0,0 +1,11 @@ +package com.stslex.atten.core.ui.navigation + +interface Navigator { + + fun navigateTo( + screen: Screen, + options: NavigatorOptions = NavigatorOptions() + ) + + fun popBack() +} diff --git a/core/navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/navigator/NavigatorImpl.kt b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavigatorImpl.kt similarity index 86% rename from core/navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/navigator/NavigatorImpl.kt rename to core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavigatorImpl.kt index 585c2ce..c513b93 100644 --- a/core/navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/navigator/NavigatorImpl.kt +++ b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavigatorImpl.kt @@ -1,9 +1,7 @@ -package com.stslex.atten.core.navigation.navigator +package com.stslex.atten.core.ui.navigation import androidx.navigation.NavHostController import com.stslex.atten.core.Logger -import com.stslex.atten.core.navigation.NavigatorOptions -import com.stslex.atten.core.navigation.Screen class NavigatorImpl( private val navHostController: NavHostController diff --git a/core/navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/NavigatorOptions.kt b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavigatorOptions.kt similarity index 61% rename from core/navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/NavigatorOptions.kt rename to core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavigatorOptions.kt index c0010af..18b114b 100644 --- a/core/navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/NavigatorOptions.kt +++ b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavigatorOptions.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.navigation +package com.stslex.atten.core.ui.navigation data class NavigatorOptions( val isSingleTop: Boolean = false, diff --git a/core/navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/Screen.kt b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Screen.kt similarity index 86% rename from core/navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/Screen.kt rename to core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Screen.kt index 3c4e14b..e672b9b 100644 --- a/core/navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/Screen.kt +++ b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Screen.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.navigation +package com.stslex.atten.core.ui.navigation import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/core/navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/di/ModuleCoreNavigation.kt b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/di/ModuleCoreNavigation.kt similarity index 70% rename from core/navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/di/ModuleCoreNavigation.kt rename to core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/di/ModuleCoreNavigation.kt index 474f886..4a0eab1 100644 --- a/core/navigation/src/commonMain/kotlin/com.stslex.atten.core.navigation/di/ModuleCoreNavigation.kt +++ b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/di/ModuleCoreNavigation.kt @@ -1,9 +1,9 @@ -package com.stslex.atten.core.navigation.di +package com.stslex.atten.core.ui.navigation.di import androidx.navigation.NavHostController import com.stslex.atten.core.di.AppModule -import com.stslex.atten.core.navigation.navigator.Navigator -import com.stslex.atten.core.navigation.navigator.NavigatorImpl +import com.stslex.atten.core.ui.navigation.Navigator +import com.stslex.atten.core.ui.navigation.NavigatorImpl import org.koin.core.annotation.Module import org.koin.dsl.module import org.koin.core.module.Module as KoinModule diff --git a/feature/details/build.gradle.kts b/feature/details/build.gradle.kts index 7225ba6..f4280fe 100644 --- a/feature/details/build.gradle.kts +++ b/feature/details/build.gradle.kts @@ -6,10 +6,10 @@ plugins { kotlin { sourceSets.commonMain.dependencies { implementation(project(":core:core")) - implementation(project(":core:ui")) + implementation(project(":core:ui:kit")) + implementation(project(":core:ui:navigation")) implementation(project(":core:database")) implementation(project(":core:todo")) - implementation(project(":core:navigation")) implementation(project(":core:paging")) implementation(libs.kotlinx.datetime) diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt index fe69d5a..e6b2b4c 100644 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt @@ -1,7 +1,7 @@ package com.stslex.atten.feature.details.di import com.stslex.atten.core.di.AppModule -import com.stslex.atten.core.ui.mvi.storeOf +import com.stslex.atten.core.ui.kit.mvi.storeOf import com.stslex.atten.feature.details.domain.interactor.DetailsInteractor import com.stslex.atten.feature.details.domain.interactor.DetailsInteractorImpl import com.stslex.atten.feature.details.navigation.DetailsRouter diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsGraph.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsGraph.kt index 0fde6c7..17fc891 100644 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsGraph.kt +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsGraph.kt @@ -5,9 +5,9 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.navigation.NavGraphBuilder -import com.stslex.atten.core.navigation.Screen -import com.stslex.atten.core.navigation.navScreen -import com.stslex.atten.core.ui.mvi.getStore +import com.stslex.atten.core.ui.navigation.Screen +import com.stslex.atten.core.ui.navigation.navScreen +import com.stslex.atten.core.ui.kit.mvi.getStore import com.stslex.atten.feature.details.ui.DetailsScreen import com.stslex.atten.feature.details.ui.store.DetailsStore import com.stslex.atten.feature.details.ui.store.DetailsStoreComponent.Action diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsRouter.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsRouter.kt index 883a209..23ba0e4 100644 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsRouter.kt +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsRouter.kt @@ -1,7 +1,7 @@ package com.stslex.atten.feature.details.navigation -import com.stslex.atten.core.navigation.navigator.Navigator -import com.stslex.atten.core.ui.mvi.Router +import com.stslex.atten.core.ui.navigation.Navigator +import com.stslex.atten.core.ui.kit.mvi.Router import com.stslex.atten.feature.details.ui.store.DetailsStoreComponent.Navigation interface DetailsRouter : Router diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/DetailsScreen.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/DetailsScreen.kt index 859abf7..804a9d6 100644 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/DetailsScreen.kt +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/DetailsScreen.kt @@ -13,8 +13,8 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import com.stslex.atten.core.ui.theme.AppDimension -import com.stslex.atten.core.ui.theme.AppTheme +import com.stslex.atten.core.ui.kit.theme.AppDimension +import com.stslex.atten.core.ui.kit.theme.AppTheme import com.stslex.atten.feature.details.ui.components.DescriptionTextField import com.stslex.atten.feature.details.ui.components.TitleTextField import com.stslex.atten.feature.details.ui.model.ToDoDetailsUIModel diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStore.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStore.kt index eed4bdb..4032f57 100644 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStore.kt +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStore.kt @@ -2,8 +2,8 @@ package com.stslex.atten.feature.details.ui.store import com.stslex.atten.core.Logger import com.stslex.atten.core.coroutine.dispatcher.AppDispatcher -import com.stslex.atten.core.ui.mvi.Store -import com.stslex.atten.core.ui.mvi.StoreComponent.Event.Snackbar +import com.stslex.atten.core.ui.kit.mvi.Store +import com.stslex.atten.core.ui.kit.mvi.StoreComponent.Event.Snackbar import com.stslex.atten.feature.details.domain.interactor.DetailsInteractor import com.stslex.atten.feature.details.navigation.DetailsRouter import com.stslex.atten.feature.details.ui.model.toDomain diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStoreComponent.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStoreComponent.kt index b4526d0..884058b 100644 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStoreComponent.kt +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStoreComponent.kt @@ -1,7 +1,7 @@ package com.stslex.atten.feature.details.ui.store import androidx.compose.runtime.Stable -import com.stslex.atten.core.ui.mvi.StoreComponent +import com.stslex.atten.core.ui.kit.mvi.StoreComponent import com.stslex.atten.feature.details.ui.model.ToDoDetailsUIModel import kotlinx.datetime.LocalDateTime diff --git a/feature/home/build.gradle.kts b/feature/home/build.gradle.kts index c3e443b..8fcc1a1 100644 --- a/feature/home/build.gradle.kts +++ b/feature/home/build.gradle.kts @@ -6,9 +6,9 @@ plugins { kotlin { sourceSets.commonMain.dependencies { implementation(project(":core:core")) - implementation(project(":core:ui")) + implementation(project(":core:ui:kit")) + implementation(project(":core:ui:navigation")) implementation(project(":core:todo")) - implementation(project(":core:navigation")) implementation(project(":core:paging")) implementation(libs.kotlinx.datetime) diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt index 2280e72..c2e62a9 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt @@ -1,7 +1,7 @@ package com.stslex.atten.feature.home.di import com.stslex.atten.core.di.AppModule -import com.stslex.atten.core.ui.mvi.storeOf +import com.stslex.atten.core.ui.kit.mvi.storeOf import com.stslex.atten.feature.home.domain.interactor.HomeScreenInteractor import com.stslex.atten.feature.home.domain.interactor.HomeScreenInteractorImpl import com.stslex.atten.feature.home.navigation.HomeRouter diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeGraph.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeGraph.kt index bd33183..0efffcd 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeGraph.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeGraph.kt @@ -7,9 +7,9 @@ import androidx.compose.runtime.remember import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalHapticFeedback import androidx.navigation.NavGraphBuilder -import com.stslex.atten.core.navigation.Screen -import com.stslex.atten.core.navigation.navScreen -import com.stslex.atten.core.ui.mvi.getStore +import com.stslex.atten.core.ui.navigation.Screen +import com.stslex.atten.core.ui.navigation.navScreen +import com.stslex.atten.core.ui.kit.mvi.getStore import com.stslex.atten.feature.home.ui.HomeScreen import com.stslex.atten.feature.home.ui.store.HomeStore import com.stslex.atten.feature.home.ui.store.HomeStoreComponent.Action diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouter.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouter.kt index a75d82e..f6018ce 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouter.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouter.kt @@ -1,6 +1,6 @@ package com.stslex.atten.feature.home.navigation -import com.stslex.atten.core.ui.mvi.Router +import com.stslex.atten.core.ui.kit.mvi.Router import com.stslex.atten.feature.home.ui.store.HomeStoreComponent.Navigation interface HomeRouter : Router diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouterImpl.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouterImpl.kt index 22de113..118c73b 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouterImpl.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouterImpl.kt @@ -1,7 +1,7 @@ package com.stslex.atten.feature.home.navigation -import com.stslex.atten.core.navigation.Screen -import com.stslex.atten.core.navigation.navigator.Navigator +import com.stslex.atten.core.ui.navigation.Screen +import com.stslex.atten.core.ui.navigation.Navigator import com.stslex.atten.feature.home.ui.store.HomeStoreComponent.Navigation class HomeRouterImpl( diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/HomeScreen.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/HomeScreen.kt index 78ae589..6488041 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/HomeScreen.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/HomeScreen.kt @@ -27,12 +27,12 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import com.stslex.atten.core.paging.states.PagingUiState import com.stslex.atten.core.paging.model.PagingConfig +import com.stslex.atten.core.paging.states.PagingUiState import com.stslex.atten.core.paging.ui.PagingColumn -import com.stslex.atten.core.ui.components.CardWithAnimatedBorder -import com.stslex.atten.core.ui.theme.AppDimension -import com.stslex.atten.core.ui.theme.AppTheme +import com.stslex.atten.core.ui.kit.components.CardWithAnimatedBorder +import com.stslex.atten.core.ui.kit.theme.AppDimension +import com.stslex.atten.core.ui.kit.theme.AppTheme import com.stslex.atten.feature.home.ui.components.HomeScreenItem import com.stslex.atten.feature.home.ui.model.TodoUiModel import com.stslex.atten.feature.home.ui.store.HomeStoreComponent.State @@ -73,7 +73,7 @@ internal fun HomeScreen( ) { index -> state.paging.items.getOrNull(index)?.let { item -> HomeScreenItem( - modifier = Modifier.animateItemPlacement(), + modifier = Modifier.animateItem(), item = item, onItemClick = onItemClicked, onItemLongClick = onItemLongCLick diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/components/HomeScreenItem.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/components/HomeScreenItem.kt index 777f78c..bc5bccf 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/components/HomeScreenItem.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/components/HomeScreenItem.kt @@ -13,9 +13,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import com.stslex.atten.core.ui.components.CardWithAnimatedBorder -import com.stslex.atten.core.ui.theme.AppDimension -import com.stslex.atten.core.ui.theme.AppTheme +import com.stslex.atten.core.ui.kit.components.CardWithAnimatedBorder +import com.stslex.atten.core.ui.kit.theme.AppDimension +import com.stslex.atten.core.ui.kit.theme.AppTheme import com.stslex.atten.feature.home.ui.model.TodoUiModel @Composable diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStore.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStore.kt index d4abccb..d26f05e 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStore.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStore.kt @@ -5,8 +5,8 @@ import com.stslex.atten.core.paging.states.PagerAction import com.stslex.atten.core.paging.states.PagerLoadState import com.stslex.atten.core.paging.states.pagingMap import com.stslex.atten.core.paging.states.toUi -import com.stslex.atten.core.ui.mvi.Store -import com.stslex.atten.core.ui.mvi.StoreComponent.Event.Snackbar.Error +import com.stslex.atten.core.ui.kit.mvi.Store +import com.stslex.atten.core.ui.kit.mvi.StoreComponent.Event.Snackbar.Error import com.stslex.atten.feature.home.domain.interactor.HomeScreenInteractor import com.stslex.atten.feature.home.domain.model.CreateTodoDomainModel import com.stslex.atten.feature.home.navigation.HomeRouter diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStoreComponent.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStoreComponent.kt index 108465d..a77f376 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStoreComponent.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStoreComponent.kt @@ -3,7 +3,7 @@ package com.stslex.atten.feature.home.ui.store import androidx.compose.runtime.Stable import com.stslex.atten.core.paging.model.PagingConfig import com.stslex.atten.core.paging.states.PagingUiState -import com.stslex.atten.core.ui.mvi.StoreComponent +import com.stslex.atten.core.ui.kit.mvi.StoreComponent import com.stslex.atten.feature.home.ui.model.TodoUiModel import kotlinx.collections.immutable.ImmutableSet import kotlinx.collections.immutable.persistentSetOf diff --git a/gradle.properties b/gradle.properties index dc33e0f..7758ba4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,4 +6,5 @@ org.gradle.configuration-cache=true kotlin.code.style=official #Android android.useAndroidX=true -android.nonTransitiveRClass=true \ No newline at end of file +android.nonTransitiveRClass=true +ksp.useKSP2=true \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3f14253..946b17e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,26 +1,24 @@ [versions] -agp = "8.5.0" -kotlin = "2.0.20" -compose-plugin = "1.6.10" -compose-compiler = "1.5.12" -androidx-activityCompose = "1.9.2" -compose-material3 = "1.3.0" -android-compose = "1.7.1" +agp = "8.10.1" +kotlin = "2.2.0" +compose-plugin = "1.8.2" +androidx-activityCompose = "1.10.1" +compose-material3 = "1.3.2" +android-compose = "1.8.2" material = "1.12.0" -compileSdk = "34" +compileSdk = "36" minSdk = "28" -targetSdk = "34" +targetSdk = "36" versionName = "1.0" versionCode = "1" -coroutine = "1.8.1" -ksp = "2.0.20-1.0.25" # https://github.com/google/ksp/releases -room = "2.7.0-alpha07" -sqlite = "2.5.0-alpha07" -koin = "3.5.6" -koin-compose = "1.1.5" -koin-ksp = "1.3.1" +coroutine = "1.10.2" +ksp = "2.2.0-2.0.2" # https://github.com/google/ksp/releases +room = "2.7.1" +sqlite = "2.5.1" +koin = "4.1.0" +koin-ksp = "2.1.0" junit = "4.13.2" mockito = "2.19.0" @@ -29,7 +27,7 @@ kermit = "2.0.3" lifecycle = "2.8.2" immutableCollection = "0.3.5" serialization = "1.7.1" -navigation = "2.8.0-alpha10" +navigation = "2.9.0-beta03" datetime = "0.6.0" [libraries] @@ -57,7 +55,7 @@ koin-android = { group = "io.insert-koin", name = "koin-android", version.ref = koin-android-compose = { group = "io.insert-koin", name = "koin-androidx-compose", version.ref = "koin" } koin-ksp-compiler = { group = "io.insert-koin", name = "koin-ksp-compiler", version.ref = "koin-ksp" } koin-annotations = { group = "io.insert-koin", name = "koin-annotations", version.ref = "koin-ksp" } -koin-compose = { group = "io.insert-koin", name = "koin-compose", version.ref = "koin-compose" } +koin-compose = { group = "io.insert-koin", name = "koin-compose", version.ref = "koin" } koin-test = { group = "io.insert-koin", name = "koin-test" } koin-test-junit = { group = "io.insert-koin", name = "koin-test-junit4" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5945241..23efcfb 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue May 21 16:54:55 MSK 2024 +#Thu Jun 26 11:59:03 MSK 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists \ No newline at end of file +zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index 4f3337c..db57c59 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,8 +19,9 @@ rootProject.name = "AtTen" include(":commonApp") include(":core:core") -include(":core:ui") -include(":core:navigation") +include(":core:ui:kit") +include(":core:ui:navigation") +include(":core:ui:mvi") include(":core:database") include(":core:paging") include(":core:todo") From aa3959814b47db0439f5d4e92bcdad44bf369a2a Mon Sep 17 00:00:00 2001 From: stslex Date: Thu, 26 Jun 2025 21:38:22 +0300 Subject: [PATCH 2/8] start refactoring mvi/navigation/kspKoinGeneration --- build-logic/convention/build.gradle.kts | 38 +++-- .../main/kotlin/KMPComposeNavigationPlugin.kt | 10 ++ .../KMPLibraryComposeConventionPlugin.kt | 28 +--- .../main/kotlin/KMPLibraryConventionPlugin.kt | 24 +-- .../src/main/kotlin/KmpConventionFeature.kt | 12 ++ .../com/stslex/atten/convention/KmpCompose.kt | 1 + .../atten/convention/KmpComposeNavigation.kt | 31 ++++ .../convention/KmpConfigureComposeLibrary.kt | 14 ++ .../atten/convention/KmpConfigureLibrary.kt | 24 +++ .../stslex/atten/convention/KotlinAndroid.kt | 2 +- .../atten/convention/KotlinMultiplatform.kt | 17 ++- commonApp/build.gradle.kts | 1 + commonApp/src/androidMain/AndroidManifest.xml | 1 + .../kotlin/com/stslex/atten/AtTenApp.kt | 22 +++ .../kotlin/com/stslex/atten/MainActivity.kt | 39 ++--- .../commonMain/kotlin/com/stslex/atten/App.kt | 30 ++-- .../kotlin/com/stslex/atten/config/KoinApp.kt | 25 --- .../kotlin/com/stslex/atten/di/AppModules.kt | 20 ++- .../stslex/atten/host/AppNavigationHost.kt | 24 +++ .../stslex/atten/host/DefaultRootComponent.kt | 56 +++++++ .../com/stslex/atten/host/RootComponent.kt | 23 +++ .../kotlin/com/stslex/atten/ui/InitialApp.kt | 4 +- .../kotlin/com/stslex/atten/InitKoin.kt | 10 ++ .../com/stslex/atten/MainViewController.kt | 9 +- .../kotlin/com/stslex/atten/core/Logger.kt | 35 ----- .../atten/core/coroutine/CoroutineExt.kt | 4 +- .../coroutine/dispatcher/AppDispatcher.kt | 1 + .../coroutine/dispatcher/AppDispatcherImpl.kt | 3 + .../core/coroutine/scope/AppCoroutineScope.kt | 60 +++++++- .../coroutine/scope/AppCoroutineScopeImpl.kt | 50 ------ .../com/stslex/atten/core/di/AppModule.kt | 8 - .../com/stslex/atten/core/di/ModuleCore.kt | 15 +- .../atten/core/logger/AppLoggerCreator.kt | 39 +++++ .../com/stslex/atten/core/logger/Log.kt | 75 +++++++++ .../com/stslex/atten/core/logger/Logger.kt | 16 ++ core/database/build.gradle.kts | 2 +- .../atten/core/database/db/AppDatabase.kt | 4 +- .../core/database/di/ModuleCoreDatabase.kt | 9 +- .../di/ModuleCorePaging.kt | 15 +- .../factory/PagerFactoryImpl.kt | 6 +- .../pager/PagerImpl.kt | 6 +- .../data/repository/ToDoRepositoryImpl.kt | 4 +- .../di/ModuleCoreToDo.kt | 7 +- core/ui/kit/build.gradle.kts | 1 + .../com/stslex/atten/core/ui/kit/mvi/Store.kt | 11 +- core/ui/mvi/build.gradle.kts | 3 + .../com/stslex/atten/core/ui/mvi/BaseStore.kt | 142 ++++++++++++++++++ .../stslex/atten/core/ui/mvi/CommonEvents.kt | 53 +++++++ .../com/stslex/atten/core/ui/mvi/Feature.kt | 22 +++ .../atten/core/ui/mvi/NavComponentScreen.kt | 21 +++ .../com/stslex/atten/core/ui/mvi/Router.kt | 6 + .../com/stslex/atten/core/ui/mvi/Store.kt | 36 +++++ .../com/stslex/atten/core/ui/mvi/StoreExt.kt | 20 +++ .../atten/core/ui/mvi/handler/Handler.kt | 10 ++ .../core/ui/mvi/handler/HandlerCreator.kt | 11 ++ .../atten/core/ui/mvi/handler/HandlerStore.kt | 73 +++++++++ .../core/ui/mvi/processor/ActionProcessor.kt | 14 ++ .../core/ui/mvi/processor/EffectsProcessor.kt | 18 +++ .../core/ui/mvi/processor/StoreProcessor.kt | 74 +++++++++ .../ui/mvi/processor/StoreProcessorImpl.kt | 31 ++++ core/ui/navigation/build.gradle.kts | 3 +- .../atten/core/ui/navigation/Component.kt | 7 + .../stslex/atten/core/ui/navigation/Config.kt | 18 +++ .../stslex/atten/core/ui/navigation/NavExt.kt | 14 -- .../atten/core/ui/navigation/Navigator.kt | 11 -- .../atten/core/ui/navigation/NavigatorImpl.kt | 36 ----- .../core/ui/navigation/NavigatorOptions.kt | 5 - .../stslex/atten/core/ui/navigation/Screen.kt | 16 -- .../stslex/atten/core/ui/navigation/Target.kt | 3 + .../ui/navigation/di/ModuleCoreNavigation.kt | 17 --- feature/details/build.gradle.kts | 4 +- .../di/FeatureDetailsModule.kt | 11 +- .../navigation/DetailsGraph.kt | 44 +++--- .../navigation/DetailsRouter.kt | 8 +- .../ui/mvi/DetailsComponent.kt | 5 + .../ui/store/DetailsStore.kt | 2 +- feature/home/build.gradle.kts | 4 +- .../di/ModuleFeatureHome.kt | 12 +- .../navigation/HomeGraph.kt | 4 +- .../navigation/HomeRouterImpl.kt | 4 +- .../ui/mvi/HomeComponent.kt | 5 + gradle/libs.versions.toml | 32 +++- iosApp/iosApp/iOSApp.swift | 6 + 83 files changed, 1196 insertions(+), 445 deletions(-) create mode 100644 build-logic/convention/src/main/kotlin/KMPComposeNavigationPlugin.kt create mode 100644 build-logic/convention/src/main/kotlin/KmpConventionFeature.kt create mode 100644 build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KmpComposeNavigation.kt create mode 100644 build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KmpConfigureComposeLibrary.kt create mode 100644 build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KmpConfigureLibrary.kt create mode 100644 commonApp/src/androidMain/kotlin/com/stslex/atten/AtTenApp.kt delete mode 100644 commonApp/src/commonMain/kotlin/com/stslex/atten/config/KoinApp.kt create mode 100644 commonApp/src/commonMain/kotlin/com/stslex/atten/host/AppNavigationHost.kt create mode 100644 commonApp/src/commonMain/kotlin/com/stslex/atten/host/DefaultRootComponent.kt create mode 100644 commonApp/src/commonMain/kotlin/com/stslex/atten/host/RootComponent.kt create mode 100644 commonApp/src/iosMain/kotlin/com/stslex/atten/InitKoin.kt delete mode 100644 core/core/src/commonMain/kotlin/com/stslex/atten/core/Logger.kt delete mode 100644 core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/scope/AppCoroutineScopeImpl.kt delete mode 100644 core/core/src/commonMain/kotlin/com/stslex/atten/core/di/AppModule.kt create mode 100644 core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/AppLoggerCreator.kt create mode 100644 core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/Log.kt create mode 100644 core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/Logger.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/BaseStore.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/CommonEvents.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/Feature.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/NavComponentScreen.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/Router.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/Store.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/StoreExt.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/Handler.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/HandlerCreator.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/HandlerStore.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/ActionProcessor.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/EffectsProcessor.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/StoreProcessor.kt create mode 100644 core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/StoreProcessorImpl.kt create mode 100644 core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Component.kt create mode 100644 core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Config.kt delete mode 100644 core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavExt.kt delete mode 100644 core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Navigator.kt delete mode 100644 core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavigatorImpl.kt delete mode 100644 core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavigatorOptions.kt delete mode 100644 core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Screen.kt create mode 100644 core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Target.kt delete mode 100644 core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/di/ModuleCoreNavigation.kt create mode 100644 feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsComponent.kt create mode 100644 feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeComponent.kt diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index e26f8b2..ed3b84b 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -21,25 +21,23 @@ tasks { gradlePlugin { plugins { - register("kotlinLibraryMultiplatform") { - id = "convention.kmp.library" - implementationClass = "KMPLibraryConventionPlugin" - } - register("kotlinApplicationMultiplatform") { - id = "convention.kmp.application" - implementationClass = "KMPApplicationConventionPlugin" - } - register("kotlinLibraryComposeMultiplatform") { - id = "convention.kmp.library.compose" - implementationClass = "KMPLibraryComposeConventionPlugin" - } - register("kotlinLibraryComposeAndroid") { - id = "convention.android.library.compose" - implementationClass = "KotlinLibraryComposePlugin" - } - register("kotlinLibraryRoom") { - id = "convention.kmp.library.room" - implementationClass = "RoomLibraryConventionPlugin" - } + setup("KMPApplicationConventionPlugin", libs.plugins.convention.kmp.application) + setup("KMPLibraryConventionPlugin", libs.plugins.convention.kmp.library.asProvider()) + setup("KMPLibraryComposeConventionPlugin", libs.plugins.convention.kmp.library.compose) + setup("KotlinLibraryComposePlugin", libs.plugins.convention.android.library.compose) + setup("RoomLibraryConventionPlugin", libs.plugins.convention.kmp.room) + setup("KMPComposeNavigationPlugin", libs.plugins.convention.kmp.navigation) + setup("KmpConventionFeature", libs.plugins.convention.kmp.feature) + } +} + +fun NamedDomainObjectContainer.setup( + className: String, + provider: Provider, +) { + val plugin = provider.get() + register(plugin.pluginId) { + id = plugin.pluginId + implementationClass = className } } \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/KMPComposeNavigationPlugin.kt b/build-logic/convention/src/main/kotlin/KMPComposeNavigationPlugin.kt new file mode 100644 index 0000000..bd2e2e1 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/KMPComposeNavigationPlugin.kt @@ -0,0 +1,10 @@ +import com.stslex.atten.convention.configureKMPComposeNavigation +import org.gradle.api.Plugin +import org.gradle.api.Project + +class KMPComposeNavigationPlugin : Plugin { + + override fun apply(target: Project) { + target.configureKMPComposeNavigation() + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/KMPLibraryComposeConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KMPLibraryComposeConventionPlugin.kt index 261faaf..385bf09 100644 --- a/build-logic/convention/src/main/kotlin/KMPLibraryComposeConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/KMPLibraryComposeConventionPlugin.kt @@ -1,34 +1,10 @@ -import AppExt.findPluginId -import AppExt.libs -import com.android.build.api.dsl.LibraryExtension -import com.stslex.atten.convention.configureKMPCompose -import com.stslex.atten.convention.configureKotlin -import com.stslex.atten.convention.configureKotlinAndroid -import com.stslex.atten.convention.configureKotlinMultiplatform -import com.stslex.atten.convention.configureKsp +import com.stslex.atten.convention.configureKMPComposeLibrary import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.kotlin.dsl.configure class KMPLibraryComposeConventionPlugin : Plugin { override fun apply(target: Project): Unit = with(target) { - with(pluginManager) { - apply(libs.findPluginId("kotlinMultiplatform")) - apply(libs.findPluginId("jetbrainsCompose")) - apply(libs.findPluginId("composeCompiler")) - apply(libs.findPluginId("kotlinCocoapods")) - apply(libs.findPluginId("androidLibrary")) - apply(libs.findPluginId("serialization")) - } - - configureKsp() - configureKMPCompose() - configureKotlinMultiplatform() - - extensions.configure { - configureKotlin() - configureKotlinAndroid(this) - } + configureKMPComposeLibrary() } } \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/KMPLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KMPLibraryConventionPlugin.kt index a516011..5c6f0c0 100644 --- a/build-logic/convention/src/main/kotlin/KMPLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/KMPLibraryConventionPlugin.kt @@ -1,30 +1,10 @@ -import AppExt.findPluginId -import AppExt.libs -import com.android.build.api.dsl.LibraryExtension -import com.stslex.atten.convention.configureKotlin -import com.stslex.atten.convention.configureKotlinAndroid -import com.stslex.atten.convention.configureKotlinMultiplatform -import com.stslex.atten.convention.configureKsp +import com.stslex.atten.convention.configureKmpLibrary import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.kotlin.dsl.configure class KMPLibraryConventionPlugin : Plugin { override fun apply(target: Project): Unit = with(target) { - with(pluginManager) { - apply(libs.findPluginId("kotlinMultiplatform")) - apply(libs.findPluginId("kotlinCocoapods")) - apply(libs.findPluginId("androidLibrary")) - apply(libs.findPluginId("serialization")) - } - - configureKsp() - configureKotlinMultiplatform() - configureKotlin() - - extensions.configure { - configureKotlinAndroid(this) - } + configureKmpLibrary() } } \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/KmpConventionFeature.kt b/build-logic/convention/src/main/kotlin/KmpConventionFeature.kt new file mode 100644 index 0000000..ebeda71 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/KmpConventionFeature.kt @@ -0,0 +1,12 @@ +import com.stslex.atten.convention.configureKMPComposeLibrary +import com.stslex.atten.convention.configureKMPComposeNavigation +import org.gradle.api.Plugin +import org.gradle.api.Project + +class KmpConventionFeature : Plugin { + + override fun apply(target: Project) = target.run { + configureKMPComposeLibrary() + configureKMPComposeNavigation() + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KmpCompose.kt b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KmpCompose.kt index e11d05b..166d33c 100644 --- a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KmpCompose.kt +++ b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KmpCompose.kt @@ -21,6 +21,7 @@ fun Project.configureKMPCompose() = extensions.configure { + sourceSets.apply { + commonMain.dependencies { + api(libs.findLibrary("decompose").get()) + api(libs.findLibrary("decompose.extensions").get()) + api(libs.findLibrary("essenty.lifecycle").get()) + api(libs.findLibrary("essenty.stateKeeper").get()) + api(libs.findLibrary("essenty.backHandler").get()) + } + iosMain.dependencies { + api(libs.findLibrary("parcelize.darwin").get()) + } + } + (this as ExtensionAware).extensions.configure { + framework { + export(libs.findLibrary("decompose").get()) + export(libs.findLibrary("essenty.lifecycle").get()) + export(libs.findLibrary("essenty.stateKeeper").get()) + export(libs.findLibrary("parcelize.darwin").get()) + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KmpConfigureComposeLibrary.kt b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KmpConfigureComposeLibrary.kt new file mode 100644 index 0000000..d01ca7f --- /dev/null +++ b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KmpConfigureComposeLibrary.kt @@ -0,0 +1,14 @@ +package com.stslex.atten.convention + +import AppExt.findPluginId +import AppExt.libs +import org.gradle.api.Project + +fun Project.configureKMPComposeLibrary() { + configureKmpLibrary() + with(pluginManager) { + apply(libs.findPluginId("jetbrainsCompose")) + apply(libs.findPluginId("composeCompiler")) + } + configureKMPCompose() +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KmpConfigureLibrary.kt b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KmpConfigureLibrary.kt new file mode 100644 index 0000000..c892932 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KmpConfigureLibrary.kt @@ -0,0 +1,24 @@ +package com.stslex.atten.convention + +import AppExt.findPluginId +import AppExt.libs +import com.android.build.api.dsl.LibraryExtension +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +fun Project.configureKmpLibrary() { + with(pluginManager) { + apply(libs.findPluginId("kotlinMultiplatform")) + apply(libs.findPluginId("kotlinCocoapods")) + apply(libs.findPluginId("androidLibrary")) + apply(libs.findPluginId("serialization")) + } + + configureKsp() + configureKotlinMultiplatform() + configureKotlin() + + extensions.configure { + configureKotlinAndroid(this) + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinAndroid.kt b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinAndroid.kt index d630b4c..3a831ea 100644 --- a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinAndroid.kt +++ b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinAndroid.kt @@ -43,7 +43,7 @@ internal fun Project.configureKotlinAndroid( dependencies { "implementation"(libs.findLibrary("koin-core").get()) - "implementation"(libs.findLibrary("koin-annotations").get()) + "implementation"(libs.findLibrary("koin-ksp-annotations").get()) "implementation"(libs.findLibrary("koin-android").get()) "implementation"(libs.findLibrary("coroutine-core").get()) "implementation"(libs.findLibrary("coroutine-android").get()) diff --git a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinMultiplatform.kt b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinMultiplatform.kt index 879b0e4..22a922d 100644 --- a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinMultiplatform.kt +++ b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinMultiplatform.kt @@ -20,19 +20,20 @@ internal fun Project.configureKotlinMultiplatform( applyDefaultHierarchyTemplate() + dependencies { + add("kspCommonMainMetadata", libs.findLibrary("koin-ksp-compiler").get()) + } + //common dependencies sourceSets.apply { - dependencies { - val koinCompiler = libs.findLibrary("koin-ksp-compiler").get() - add("kspAndroid", koinCompiler) - add("kspIosSimulatorArm64", koinCompiler) - add("kspIosX64", koinCompiler) - add("kspIosArm64", koinCompiler) - } commonMain { + kotlin.srcDirs( + "build/generated/ksp/metadata/commonMain/kotlin", + "build/generated/ksp/commonMain/kotlin" + ) dependencies { implementation(libs.findLibrary("koin-core").get()) - implementation(libs.findLibrary("koin-annotations").get()) + implementation(libs.findLibrary("koin-ksp-annotations").get()) implementation(libs.findLibrary("coroutine-core").get()) implementation(libs.findLibrary("kotlinx-serialization-json").get()) } diff --git a/commonApp/build.gradle.kts b/commonApp/build.gradle.kts index 5a3c5a2..aedfaa1 100644 --- a/commonApp/build.gradle.kts +++ b/commonApp/build.gradle.kts @@ -1,5 +1,6 @@ plugins { alias(libs.plugins.convention.kmp.application) + alias(libs.plugins.convention.kmp.navigation) } kotlin { diff --git a/commonApp/src/androidMain/AndroidManifest.xml b/commonApp/src/androidMain/AndroidManifest.xml index 56295b5..4deb5b4 100644 --- a/commonApp/src/androidMain/AndroidManifest.xml +++ b/commonApp/src/androidMain/AndroidManifest.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools"> windowController.isAppearanceLightStatusBars = isDarkTheme.not() } - ) { - androidLogger( - level = if (isDebug) { - Level.DEBUG - } else { - Level.NONE - } - ) - androidContext(this@MainActivity) - } + ) } } +} - override fun onDestroy() { - stopKoin() - super.onDestroy() - } +@Preview +@Composable +fun AppAndroidPreview() { + App( + rootComponent = DefaultRootComponent( + componentContext = DefaultComponentContext( + LocalLifecycleOwner.current.lifecycle + ), + ), + ) } diff --git a/commonApp/src/commonMain/kotlin/com/stslex/atten/App.kt b/commonApp/src/commonMain/kotlin/com/stslex/atten/App.kt index 1653fdf..d8d92fb 100644 --- a/commonApp/src/commonMain/kotlin/com/stslex/atten/App.kt +++ b/commonApp/src/commonMain/kotlin/com/stslex/atten/App.kt @@ -1,22 +1,32 @@ package com.stslex.atten +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable -import com.stslex.atten.config.KoinApp +import androidx.compose.ui.Modifier import com.stslex.atten.core.ui.kit.theme.AppTheme -import com.stslex.atten.ui.InitialApp -import org.koin.core.KoinApplication +import com.stslex.atten.host.AppNavigationHost +import com.stslex.atten.host.RootComponent @Composable fun App( + rootComponent: RootComponent, onThemeChange: (isDark: Boolean) -> Unit = {}, - additionalSetup: KoinApplication.() -> Unit = {}, ) { - KoinApp(additionalSetup) { controller -> - AppTheme( - onThemeChange = onThemeChange - ) { - InitialApp( - navHostController = controller + AppTheme { + Scaffold( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background), + ) { paddingValues -> + AppNavigationHost( + modifier = Modifier.padding( + bottom = paddingValues.calculateBottomPadding() + ), + rootComponent = rootComponent ) } } diff --git a/commonApp/src/commonMain/kotlin/com/stslex/atten/config/KoinApp.kt b/commonApp/src/commonMain/kotlin/com/stslex/atten/config/KoinApp.kt deleted file mode 100644 index 1ce3290..0000000 --- a/commonApp/src/commonMain/kotlin/com/stslex/atten/config/KoinApp.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.stslex.atten.config - -import androidx.compose.runtime.Composable -import androidx.navigation.NavHostController -import androidx.navigation.compose.rememberNavController -import com.stslex.atten.di.appModules -import org.koin.compose.KoinApplication -import org.koin.core.KoinApplication - -@Composable -fun KoinApp( - additionalSetup: KoinApplication.() -> Unit = {}, - content: @Composable (navHostController: NavHostController) -> Unit -) { - val navHostController = rememberNavController() - KoinApplication( - application = { - modules(appModules(navHostController)) - additionalSetup() - }, - content = { - content(navHostController) - } - ) -} diff --git a/commonApp/src/commonMain/kotlin/com/stslex/atten/di/AppModules.kt b/commonApp/src/commonMain/kotlin/com/stslex/atten/di/AppModules.kt index 4390468..c977113 100644 --- a/commonApp/src/commonMain/kotlin/com/stslex/atten/di/AppModules.kt +++ b/commonApp/src/commonMain/kotlin/com/stslex/atten/di/AppModules.kt @@ -1,23 +1,27 @@ package com.stslex.atten.di -import androidx.navigation.NavHostController import com.stslex.atten.core.database.di.ModuleCoreDatabase import com.stslex.atten.core.di.ModuleCore -import com.stslex.atten.core.ui.navigation.di.ModuleCoreNavigation import com.stslex.atten.core.paging.di.ModuleCorePaging import com.stslex.atten.core.todo.di.ModuleCoreToDo import com.stslex.atten.feature.details.di.FeatureDetailsModule import com.stslex.atten.feature.home.di.ModuleFeatureHome import org.koin.core.module.Module -fun appModules( - navController: NavHostController -): List = listOf( - ModuleCore(), +val appModules: List = listOf( + ModuleCore().module, ModuleCoreDatabase(), ModuleCoreToDo(), ModuleCorePaging(), ModuleFeatureHome(), FeatureDetailsModule(), - ModuleCoreNavigation(navController) -).map { it.module } \ No newline at end of file +).map { it.module } + +val refactor = listOf( + ModuleCore().module, + ModuleCoreDatabase().module, + ModuleCoreToDo().module, + ModuleCorePaging().module, + ModuleFeatureHome().module, + FeatureDetailsModule().module, +) \ No newline at end of file diff --git a/commonApp/src/commonMain/kotlin/com/stslex/atten/host/AppNavigationHost.kt b/commonApp/src/commonMain/kotlin/com/stslex/atten/host/AppNavigationHost.kt new file mode 100644 index 0000000..3211204 --- /dev/null +++ b/commonApp/src/commonMain/kotlin/com/stslex/atten/host/AppNavigationHost.kt @@ -0,0 +1,24 @@ +package com.stslex.atten.host + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.arkivanov.decompose.extensions.compose.stack.Children +import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation + +@Composable +internal fun AppNavigationHost( + rootComponent: RootComponent, + modifier: Modifier = Modifier, +) { + Children( + stack = rootComponent.stack, + modifier = modifier.fillMaxSize(), + animation = stackAnimation(), + ) { created -> + when (val instance = created.instance) { + is RootComponent.Child.Details -> TODO() + is RootComponent.Child.Home -> TODO() + } + } +} \ No newline at end of file diff --git a/commonApp/src/commonMain/kotlin/com/stslex/atten/host/DefaultRootComponent.kt b/commonApp/src/commonMain/kotlin/com/stslex/atten/host/DefaultRootComponent.kt new file mode 100644 index 0000000..6969aab --- /dev/null +++ b/commonApp/src/commonMain/kotlin/com/stslex/atten/host/DefaultRootComponent.kt @@ -0,0 +1,56 @@ +package com.stslex.atten.host + +import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.DelicateDecomposeApi +import com.arkivanov.decompose.router.stack.ChildStack +import com.arkivanov.decompose.router.stack.StackNavigation +import com.arkivanov.decompose.router.stack.childStack +import com.arkivanov.decompose.router.stack.navigate +import com.arkivanov.decompose.router.stack.pop +import com.arkivanov.decompose.value.Value +import com.stslex.atten.core.ui.navigation.Config +import com.stslex.atten.host.RootComponent.Child + +class DefaultRootComponent( + componentContext: ComponentContext +) : RootComponent, ComponentContext by componentContext { + + private val navigation = StackNavigation() + + private val _stack = childStack( + source = navigation, + serializer = Config.serializer(), + childFactory = ::child, + handleBackButton = true, + initialConfiguration = Config.Home + ) + + override val stack: Value> = _stack + + override fun onConfigChanged(block: (Config) -> Unit) = stack.subscribe { + block(it.active.configuration) + } + + private fun child( + config: Config, + context: ComponentContext + ): Child = when (config) { + is Config.Home -> Child.Details(TODO()) + is Config.Detail -> Child.Home(TODO()) + } + + @OptIn(DelicateDecomposeApi::class) + private fun navigateTo(config: Config) { + navigation.navigate { currentStack -> + if (config.isBackAllow) { + currentStack + config + } else { + listOf(config) + } + } + } + + private fun popBack() { + navigation.pop() + } +} diff --git a/commonApp/src/commonMain/kotlin/com/stslex/atten/host/RootComponent.kt b/commonApp/src/commonMain/kotlin/com/stslex/atten/host/RootComponent.kt new file mode 100644 index 0000000..9644bae --- /dev/null +++ b/commonApp/src/commonMain/kotlin/com/stslex/atten/host/RootComponent.kt @@ -0,0 +1,23 @@ +package com.stslex.atten.host + +import com.arkivanov.decompose.Cancellation +import com.arkivanov.decompose.router.stack.ChildStack +import com.arkivanov.decompose.value.Value +import com.stslex.atten.core.ui.navigation.Config +import com.stslex.atten.feature.details.ui.mvi.DetailsComponent +import com.stslex.atten.feature.home.ui.mvi.HomeComponent + +interface RootComponent { + + val stack: Value> + + fun onConfigChanged(block: (Config) -> Unit): Cancellation + + sealed interface Child { + + data class Home(val component: HomeComponent) : Child + + data class Details(val component: DetailsComponent) : Child + } + +} \ No newline at end of file diff --git a/commonApp/src/commonMain/kotlin/com/stslex/atten/ui/InitialApp.kt b/commonApp/src/commonMain/kotlin/com/stslex/atten/ui/InitialApp.kt index bd513e5..b8502a9 100644 --- a/commonApp/src/commonMain/kotlin/com/stslex/atten/ui/InitialApp.kt +++ b/commonApp/src/commonMain/kotlin/com/stslex/atten/ui/InitialApp.kt @@ -6,7 +6,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost -import com.stslex.atten.core.ui.navigation.Screen +import com.stslex.atten.core.ui.navigation.Component import com.stslex.atten.feature.details.navigation.detailsGraph import com.stslex.atten.feature.home.navigation.homeGraph @@ -20,7 +20,7 @@ internal fun InitialApp( ) { NavHost( navController = navHostController, - startDestination = Screen.Home + startDestination = Component.Home ) { homeGraph() detailsGraph() diff --git a/commonApp/src/iosMain/kotlin/com/stslex/atten/InitKoin.kt b/commonApp/src/iosMain/kotlin/com/stslex/atten/InitKoin.kt new file mode 100644 index 0000000..453a499 --- /dev/null +++ b/commonApp/src/iosMain/kotlin/com/stslex/atten/InitKoin.kt @@ -0,0 +1,10 @@ +package com.stslex.atten + +import com.stslex.atten.di.appModules +import org.koin.core.context.startKoin + +fun InitKoin() { + startKoin { + modules(appModules) + } +} \ No newline at end of file diff --git a/commonApp/src/iosMain/kotlin/com/stslex/atten/MainViewController.kt b/commonApp/src/iosMain/kotlin/com/stslex/atten/MainViewController.kt index 67ecff3..1c0fcb3 100644 --- a/commonApp/src/iosMain/kotlin/com/stslex/atten/MainViewController.kt +++ b/commonApp/src/iosMain/kotlin/com/stslex/atten/MainViewController.kt @@ -1,7 +1,14 @@ package com.stslex.atten +import androidx.compose.runtime.remember import androidx.compose.ui.window.ComposeUIViewController +import com.arkivanov.decompose.DefaultComponentContext +import com.arkivanov.essenty.lifecycle.ApplicationLifecycle +import com.stslex.atten.host.DefaultRootComponent fun MainViewController() = ComposeUIViewController { - App() + val root = remember { + DefaultRootComponent(DefaultComponentContext(ApplicationLifecycle())) + } + App(root) } diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/Logger.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/Logger.kt deleted file mode 100644 index fd16c4b..0000000 --- a/core/core/src/commonMain/kotlin/com/stslex/atten/core/Logger.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.stslex.atten.core - -import com.stslex.atten.core.common.isDebug -import co.touchlab.kermit.Logger as Log - -object Logger { - - private const val DEFAULT_TAG = "WIZARD" - - fun e( - throwable: Throwable, - tag: String? = null, - message: String? = null - ) { - if (isDebug.not()) return - // todo firebase crashlytics - Log.e( - tag = tag ?: DEFAULT_TAG, - throwable = throwable, - messageString = message ?: throwable.message.orEmpty(), - ) - } - - fun d( - message: String, - tag: String? = null, - ) { - if (isDebug.not()) return - // todo firebase analytics - Log.d( - tag = tag ?: DEFAULT_TAG, - messageString = message - ) - } -} \ No newline at end of file diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/CoroutineExt.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/CoroutineExt.kt index 94d1eae..249a6e2 100644 --- a/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/CoroutineExt.kt +++ b/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/CoroutineExt.kt @@ -1,13 +1,13 @@ package com.stslex.atten.core.coroutine -import com.stslex.atten.core.Logger +import com.stslex.atten.core.logger.Log import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable -> - Logger.e(throwable) + Log.e(throwable) } suspend fun List.asyncMap( diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/dispatcher/AppDispatcher.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/dispatcher/AppDispatcher.kt index e6f80d5..4707e60 100644 --- a/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/dispatcher/AppDispatcher.kt +++ b/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/dispatcher/AppDispatcher.kt @@ -7,4 +7,5 @@ interface AppDispatcher { val default: CoroutineDispatcher val io: CoroutineDispatcher val main: MainCoroutineDispatcher + val immediate: MainCoroutineDispatcher } diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/dispatcher/AppDispatcherImpl.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/dispatcher/AppDispatcherImpl.kt index 1361259..9e889ec 100644 --- a/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/dispatcher/AppDispatcherImpl.kt +++ b/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/dispatcher/AppDispatcherImpl.kt @@ -4,9 +4,12 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.IO import kotlinx.coroutines.MainCoroutineDispatcher +import org.koin.core.annotation.Singleton +@Singleton class AppDispatcherImpl : AppDispatcher { override val default: CoroutineDispatcher = Dispatchers.Default override val io: CoroutineDispatcher = Dispatchers.IO override val main: MainCoroutineDispatcher = Dispatchers.Main + override val immediate: MainCoroutineDispatcher = Dispatchers.Main.immediate } \ No newline at end of file diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/scope/AppCoroutineScope.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/scope/AppCoroutineScope.kt index f3eda06..f911331 100644 --- a/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/scope/AppCoroutineScope.kt +++ b/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/scope/AppCoroutineScope.kt @@ -1,12 +1,34 @@ package com.stslex.atten.core.coroutine.scope import com.stslex.atten.core.coroutine.dispatcher.AppDispatcher +import com.stslex.atten.core.logger.Log +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext -interface AppCoroutineScope { +class AppCoroutineScope( + private val scope: CoroutineScope, + private val appDispatcher: AppDispatcher, +) { + + private fun exceptionHandler( + eachDispatcher: CoroutineDispatcher, + onError: suspend (cause: Throwable) -> Unit = {}, + ) = CoroutineExceptionHandler { _, throwable -> + Log.e(throwable) + scope.launch(eachDispatcher) { + onError(throwable) + } + } /** * Launches a coroutine and catches exceptions. The coroutine is launched on the default dispatcher of the AppDispatcher. @@ -21,8 +43,27 @@ interface AppCoroutineScope { start: CoroutineStart = CoroutineStart.DEFAULT, onError: suspend (Throwable) -> Unit = {}, onSuccess: suspend CoroutineScope.(T) -> Unit = {}, + workDispatcher: CoroutineDispatcher = appDispatcher.default, + eachDispatcher: CoroutineDispatcher = appDispatcher.immediate, + exceptionHandler: CoroutineExceptionHandler = exceptionHandler(eachDispatcher, onError), action: suspend CoroutineScope.() -> T, - ): Job + ): Job = scope.launch( + start = start, + context = workDispatcher + exceptionHandler, + block = { + runCatching { action() } + .onSuccess { + withContext(eachDispatcher) { + onSuccess(it) + } + } + .onFailure { + withContext(eachDispatcher) { + onError(it) + } + } + } + ) /** * Launches a flow and collects it in the screenModelScope. The flow is collected on the default dispatcher. of the AppDispatcher. @@ -35,8 +76,17 @@ interface AppCoroutineScope { * */ fun launch( flow: Flow, + workDispatcher: CoroutineDispatcher = appDispatcher.default, + eachDispatcher: CoroutineDispatcher = appDispatcher.immediate, onError: suspend (cause: Throwable) -> Unit = {}, - each: suspend (T) -> Unit - ): Job + each: suspend (T) -> Unit, + ): Job = flow + .catch { onError(it) } + .onEach { + withContext(eachDispatcher) { + each(it) + } + } + .flowOn(workDispatcher) + .launchIn(scope) } - diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/scope/AppCoroutineScopeImpl.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/scope/AppCoroutineScopeImpl.kt deleted file mode 100644 index 9e27f40..0000000 --- a/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/scope/AppCoroutineScopeImpl.kt +++ /dev/null @@ -1,50 +0,0 @@ -package com.stslex.atten.core.coroutine.scope - -import com.stslex.atten.core.Logger -import com.stslex.atten.core.coroutine.coroutineExceptionHandler -import com.stslex.atten.core.coroutine.dispatcher.AppDispatcher -import kotlinx.coroutines.CoroutineExceptionHandler -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.CoroutineStart -import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.launch - -class AppCoroutineScopeImpl( - private val scope: CoroutineScope, - private val appDispatcher: AppDispatcher -) : AppCoroutineScope { - - private fun exceptionHandler( - onError: suspend (cause: Throwable) -> Unit = {}, - ) = CoroutineExceptionHandler { _, throwable -> - Logger.e(throwable) - scope.launch(appDispatcher.default + coroutineExceptionHandler) { - onError(throwable) - } - } - - override fun launch( - start: CoroutineStart, - onError: suspend (Throwable) -> Unit, - onSuccess: suspend CoroutineScope.(T) -> Unit, - action: suspend CoroutineScope.() -> T - ): Job = scope.launch( - start = start, - context = exceptionHandler(onError) + appDispatcher.default, - block = { - onSuccess(action()) - } - ) - - override fun launch( - flow: Flow, - onError: suspend (cause: Throwable) -> Unit, - each: suspend (T) -> Unit - ): Job = scope.launch( - context = exceptionHandler(onError) + appDispatcher.default, - block = { - flow.collect(each) - } - ) -} \ No newline at end of file diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/di/AppModule.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/di/AppModule.kt deleted file mode 100644 index 44036fc..0000000 --- a/core/core/src/commonMain/kotlin/com/stslex/atten/core/di/AppModule.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.stslex.atten.core.di - -import org.koin.core.module.Module - -interface AppModule { - - val module: Module -} \ No newline at end of file diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/di/ModuleCore.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/di/ModuleCore.kt index 5d271e7..ced4145 100644 --- a/core/core/src/commonMain/kotlin/com/stslex/atten/core/di/ModuleCore.kt +++ b/core/core/src/commonMain/kotlin/com/stslex/atten/core/di/ModuleCore.kt @@ -1,19 +1,8 @@ package com.stslex.atten.core.di -import com.stslex.atten.core.coroutine.dispatcher.AppDispatcher -import com.stslex.atten.core.coroutine.dispatcher.AppDispatcherImpl import org.koin.core.annotation.ComponentScan import org.koin.core.annotation.Module -import org.koin.core.module.dsl.bind -import org.koin.core.module.dsl.singleOf -import org.koin.dsl.module -import org.koin.core.module.Module as KoinModule @Module -@ComponentScan -class ModuleCore : AppModule { - - override val module: KoinModule = module { - singleOf(::AppDispatcherImpl) { bind() } - } -} \ No newline at end of file +@ComponentScan("com.stslex.atten.core") +class ModuleCore \ No newline at end of file diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/AppLoggerCreator.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/AppLoggerCreator.kt new file mode 100644 index 0000000..556a058 --- /dev/null +++ b/core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/AppLoggerCreator.kt @@ -0,0 +1,39 @@ +package com.stslex.atten.core.logger + +internal class AppLoggerCreator( + private val tag: String +) : Logger { + + override fun d(message: String) { + Log.d( + tag = tag, + message = message + ) + } + + override fun e( + throwable: Throwable, + message: String? + ) { + Log.e( + tag = tag, + throwable = throwable, + message = message ?: throwable.message.orEmpty(), + ) + } + + override fun i(message: String) { + Log.i( + tag = tag, + message = message + ) + } + + override fun v(message: String) { + Log.v( + tag = tag, + message = message + ) + } + +} \ No newline at end of file diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/Log.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/Log.kt new file mode 100644 index 0000000..5981302 --- /dev/null +++ b/core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/Log.kt @@ -0,0 +1,75 @@ +package com.stslex.atten.core.logger + +import co.touchlab.kermit.Logger +import com.stslex.atten.core.common.isDebug +import com.stslex.atten.core.logger.Logger as AtTenLogger + +object Log : AtTenLogger { + + private const val DEFAULT_TAG = "AtTen" + + fun tag(tag: String): AtTenLogger = AppLoggerCreator(tag) + + fun e( + throwable: Throwable, + tag: String? = null, + message: String? = null + ) { + if (isDebug.not()) return + // todo firebase crashlytics + Logger.Companion.e( + tag = tag ?: DEFAULT_TAG, + throwable = throwable, + messageString = message ?: throwable.message.orEmpty(), + ) + } + + fun d( + message: String, + tag: String? = null, + ) { + if (isDebug.not()) return + Logger.Companion.d( + tag = tag ?: DEFAULT_TAG, + messageString = message + ) + } + + fun i( + message: String, + tag: String? = null, + ) { + if (isDebug.not()) return + Logger.Companion.i( + tag = tag ?: DEFAULT_TAG, + messageString = message + ) + } + + fun v( + message: String, + tag: String? = null, + ) { + if (isDebug.not()) return + Logger.Companion.v( + tag = tag ?: DEFAULT_TAG, + messageString = message + ) + } + + override fun e(throwable: Throwable, message: String?) { + e(throwable = throwable, tag = DEFAULT_TAG, message = message) + } + + override fun d(message: String) { + d(message = message, tag = DEFAULT_TAG) + } + + override fun i(message: String) { + i(message = message, tag = DEFAULT_TAG) + } + + override fun v(message: String) { + v(message = message, tag = DEFAULT_TAG) + } +} \ No newline at end of file diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/Logger.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/Logger.kt new file mode 100644 index 0000000..180cd42 --- /dev/null +++ b/core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/Logger.kt @@ -0,0 +1,16 @@ +package com.stslex.atten.core.logger + +interface Logger { + + fun e( + throwable: Throwable, + message: String? = null + ) + + fun d(message: String) + + fun i(message: String) + + fun v(message: String) +} + diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index 7c55158..149c635 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -1,6 +1,6 @@ plugins { alias(libs.plugins.convention.kmp.library) - alias(libs.plugins.convention.kmp.library.room) + alias(libs.plugins.convention.kmp.room) } kotlin { diff --git a/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/db/AppDatabase.kt b/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/db/AppDatabase.kt index fe7ef3e..092dcfe 100644 --- a/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/db/AppDatabase.kt +++ b/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/db/AppDatabase.kt @@ -28,7 +28,9 @@ abstract class AppDatabase : RoomDatabase() { // The Room compiler generates the `actual` implementations. @Suppress("NO_ACTUAL_FOR_EXPECT") -expect object AppDatabaseConstructor : RoomDatabaseConstructor +expect object AppDatabaseConstructor : RoomDatabaseConstructor { + override fun initialize(): AppDatabase +} fun getRoomDatabase( builder: RoomDatabase.Builder diff --git a/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/di/ModuleCoreDatabase.kt b/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/di/ModuleCoreDatabase.kt index 7eaab49..9d28bf7 100644 --- a/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/di/ModuleCoreDatabase.kt +++ b/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/di/ModuleCoreDatabase.kt @@ -2,17 +2,16 @@ package com.stslex.atten.core.database.di import com.stslex.atten.core.database.db.AppDatabase import com.stslex.atten.core.database.db.ToDoDao -import com.stslex.atten.core.di.AppModule +import org.koin.core.annotation.ComponentScan import org.koin.core.annotation.Module -import org.koin.core.annotation.Singleton import org.koin.core.scope.Scope import org.koin.dsl.module @Module -@Singleton -class ModuleCoreDatabase : AppModule { +@ComponentScan("com.stslex.atten.core.database") +class ModuleCoreDatabase { - override val module = module { + val module = module { single { getDatabase() } single { get().getTodoDao() } } diff --git a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/di/ModuleCorePaging.kt b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/di/ModuleCorePaging.kt index 4f3da20..b1ba733 100644 --- a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/di/ModuleCorePaging.kt +++ b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/di/ModuleCorePaging.kt @@ -1,17 +1,8 @@ package com.stslex.atten.core.paging.di -import com.stslex.atten.core.di.AppModule -import com.stslex.atten.core.paging.factory.PagerFactory -import com.stslex.atten.core.paging.factory.PagerFactoryImpl +import org.koin.core.annotation.ComponentScan import org.koin.core.annotation.Module -import org.koin.core.module.dsl.bind -import org.koin.core.module.dsl.singleOf -import org.koin.dsl.module @Module -class ModuleCorePaging : AppModule { - - override val module = module { - singleOf(::PagerFactoryImpl) { bind() } - } -} \ No newline at end of file +@ComponentScan("com.stslex.atten.core.paging") +class ModuleCorePaging \ No newline at end of file diff --git a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/factory/PagerFactoryImpl.kt b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/factory/PagerFactoryImpl.kt index a4b14cd..7841458 100644 --- a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/factory/PagerFactoryImpl.kt +++ b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/factory/PagerFactoryImpl.kt @@ -1,14 +1,16 @@ package com.stslex.atten.core.paging.factory import com.stslex.atten.core.coroutine.scope.AppCoroutineScope -import com.stslex.atten.core.paging.model.PagingItem -import com.stslex.atten.core.paging.model.PagingResponse import com.stslex.atten.core.paging.holder.ItemHolder import com.stslex.atten.core.paging.model.PagingConfig +import com.stslex.atten.core.paging.model.PagingItem +import com.stslex.atten.core.paging.model.PagingResponse import com.stslex.atten.core.paging.pager.Pager import com.stslex.atten.core.paging.pager.PagerImpl import com.stslex.atten.core.paging.worker.PagingWorkerImpl +import org.koin.core.annotation.Single +@Single class PagerFactoryImpl : PagerFactory { override fun create( diff --git a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/pager/PagerImpl.kt b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/pager/PagerImpl.kt index dcb3996..e620b88 100644 --- a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/pager/PagerImpl.kt +++ b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/pager/PagerImpl.kt @@ -1,7 +1,7 @@ package com.stslex.atten.core.paging.pager -import com.stslex.atten.core.Logger import com.stslex.atten.core.coroutine.scope.AppCoroutineScope +import com.stslex.atten.core.logger.Log import com.stslex.atten.core.paging.holder.ItemHolder import com.stslex.atten.core.paging.holder.ItemLoaderEvent import com.stslex.atten.core.paging.model.PagingConfig @@ -44,7 +44,7 @@ class PagerImpl( init { scope.launch(holder.event) { event -> - Logger.d("holder event: $event", TAG) + Log.d("holder event: $event", TAG) when (event) { is ItemLoaderEvent.Create -> onItemCreated(event) is ItemLoaderEvent.Insert -> onItemInserted(event) @@ -56,7 +56,7 @@ class PagerImpl( } override fun process(action: PagerAction) { - Logger.d("process action: $action", TAG) + Log.d("process action: $action", TAG) when (action) { is PagerAction.Initial -> processInitial() is PagerAction.Load -> processLoad(action.isForce) diff --git a/core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/data/repository/ToDoRepositoryImpl.kt b/core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/data/repository/ToDoRepositoryImpl.kt index 5893935..aef5de3 100644 --- a/core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/data/repository/ToDoRepositoryImpl.kt +++ b/core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/data/repository/ToDoRepositoryImpl.kt @@ -3,7 +3,7 @@ package com.stslex.atten.core.todo.data.repository import com.stslex.atten.core.coroutine.asyncMap import com.stslex.atten.core.coroutine.coroutineExceptionHandler import com.stslex.atten.core.coroutine.dispatcher.AppDispatcher -import com.stslex.atten.core.coroutine.scope.AppCoroutineScopeImpl +import com.stslex.atten.core.coroutine.scope.AppCoroutineScope import com.stslex.atten.core.database.db.ToDoDao import com.stslex.atten.core.paging.factory.PagerFactory import com.stslex.atten.core.paging.holder.ItemHolder @@ -36,7 +36,7 @@ class ToDoRepositoryImpl( private val pager by lazy { pagerFactory.create( - scope = AppCoroutineScopeImpl( + scope = AppCoroutineScope( scope = CoroutineScope(appDispatcher.io + SupervisorJob() + coroutineExceptionHandler), appDispatcher = appDispatcher ), diff --git a/core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/di/ModuleCoreToDo.kt b/core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/di/ModuleCoreToDo.kt index 2755f11..fba6b26 100644 --- a/core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/di/ModuleCoreToDo.kt +++ b/core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/di/ModuleCoreToDo.kt @@ -1,20 +1,21 @@ package com.stslex.atten.core.todo.di -import com.stslex.atten.core.di.AppModule import com.stslex.atten.core.paging.holder.ItemHolder import com.stslex.atten.core.paging.holder.ItemHolderImpl import com.stslex.atten.core.todo.data.model.ToDoDataModel import com.stslex.atten.core.todo.data.repository.ToDoRepository import com.stslex.atten.core.todo.data.repository.ToDoRepositoryImpl +import org.koin.core.annotation.ComponentScan import org.koin.core.annotation.Module import org.koin.core.module.dsl.bind import org.koin.core.module.dsl.singleOf import org.koin.dsl.module @Module -class ModuleCoreToDo : AppModule { +@ComponentScan("com.stslex.atten.core.todo") +class ModuleCoreToDo { - override val module = module { + val module = module { single> { ItemHolderImpl() } singleOf(::ToDoRepositoryImpl) { bind() } } diff --git a/core/ui/kit/build.gradle.kts b/core/ui/kit/build.gradle.kts index e554771..a2d8b2b 100644 --- a/core/ui/kit/build.gradle.kts +++ b/core/ui/kit/build.gradle.kts @@ -6,6 +6,7 @@ plugins { kotlin { sourceSets.apply { commonMain.dependencies { + implementation(project(":core:core")) implementation(project(":core:core")) implementation(compose.material) } diff --git a/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/Store.kt b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/Store.kt index b5ff7b5..c74b7ff 100644 --- a/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/Store.kt +++ b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/Store.kt @@ -2,10 +2,9 @@ package com.stslex.atten.core.ui.kit.mvi import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.stslex.atten.core.Logger import com.stslex.atten.core.coroutine.dispatcher.AppDispatcher import com.stslex.atten.core.coroutine.scope.AppCoroutineScope -import com.stslex.atten.core.coroutine.scope.AppCoroutineScopeImpl +import com.stslex.atten.core.logger.Log import com.stslex.atten.core.ui.kit.mvi.StoreComponent.Action import com.stslex.atten.core.ui.kit.mvi.StoreComponent.Event import com.stslex.atten.core.ui.kit.mvi.StoreComponent.Navigation @@ -35,7 +34,7 @@ abstract class Store( private val _state: MutableStateFlow = MutableStateFlow(initialState) override val state: StateFlow = _state.asStateFlow() - protected val scope: AppCoroutineScope = AppCoroutineScopeImpl( + protected val scope: AppCoroutineScope = AppCoroutineScope( scope = viewModelScope, appDispatcher = appDispatcher ) @@ -48,7 +47,7 @@ abstract class Store( if (lastAction != action && action !is Action.RepeatLastAction) { _lastAction = action } - Logger.d("$screenName dispatchAction: $action", TAG) + Log.d("$screenName dispatchAction: $action", TAG) process(action) } @@ -69,7 +68,7 @@ abstract class Store( * @see AppDispatcher * */ protected fun sendEvent(event: E) { - Logger.d("$screenName sendEvent: $event", TAG) + Log.d("$screenName sendEvent: $event", TAG) scope.launch { this@Store._event.emit(event) } @@ -81,7 +80,7 @@ abstract class Store( * @see Router * */ protected fun consumeNavigation(event: N) { - Logger.d("$screenName consumeNavigation: $event", TAG) + Log.d("$screenName consumeNavigation: $event", TAG) router(event) } diff --git a/core/ui/mvi/build.gradle.kts b/core/ui/mvi/build.gradle.kts index 3e72290..dc1fab4 100644 --- a/core/ui/mvi/build.gradle.kts +++ b/core/ui/mvi/build.gradle.kts @@ -5,5 +5,8 @@ plugins { kotlin { sourceSets.commonMain.dependencies { implementation(project(":core:core")) + implementation(project(":core:ui:navigation")) + implementation(project(":core:ui:kit")) + implementation(libs.decompose) } } \ No newline at end of file diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/BaseStore.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/BaseStore.kt new file mode 100644 index 0000000..9c5beef --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/BaseStore.kt @@ -0,0 +1,142 @@ +package com.stslex.atten.core.ui.mvi + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.stslex.atten.core.coroutine.dispatcher.AppDispatcher +import com.stslex.atten.core.coroutine.scope.AppCoroutineScope +import com.stslex.atten.core.logger.Log +import com.stslex.atten.core.ui.mvi.Store.Action +import com.stslex.atten.core.ui.mvi.Store.Event +import com.stslex.atten.core.ui.mvi.Store.State +import com.stslex.atten.core.ui.mvi.handler.HandlerStore +import com.stslex.wizard.core.ui.mvi.v2.Handler +import com.stslex.wizard.core.ui.mvi.v2.HandlerCreator +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update + +/** + * Base class for creating a store, which manages the state and events of a screen or feature. + * It follows a unidirectional data flow pattern, where actions are consumed, leading to state updates and/or events being emitted. + * + * @param S The type of the state held by the store. + * @param A The type of actions that can be consumed by the store. + * @param E The type of events that can be emitted by the store. + * @param HStore The type of the handler store, which provides context to action handlers. Must inherit from [handler.HandlerStore]. + * @param name A descriptive name for the store, used for logging. + * @param initialState The initial state of the store. + * @param handlerCreator A factory function that creates an [com.stslex.wizard.core.ui.mvi.v2.Handler] for a given action. + * @param initialActions A list of actions to be consumed immediately after the store is initialized. Defaults to an empty list. + */ +open class BaseStore>( + name: String, + initialState: S, + override val appDispatcher: AppDispatcher, + private val handlerCreator: HandlerCreator, + initialActions: List = emptyList(), +) : ViewModel(), Store, HandlerStore { + + private val _event: MutableSharedFlow = MutableSharedFlow() + override val event: SharedFlow = _event.asSharedFlow() + + private val _state: MutableStateFlow = MutableStateFlow(initialState) + override val state: StateFlow = _state.asStateFlow() + + override val scope: AppCoroutineScope = AppCoroutineScope(viewModelScope, appDispatcher) + override val logger = Log.tag(name) + + private var _lastAction: A? = null + override val lastAction: A? + get() = _lastAction + + init { + initialActions.forEach { consume(it) } + } + + @Suppress("UNCHECKED_CAST") + override fun consume(action: A) { + logger.i("consume: $action") + if (lastAction != action && action !is Action.RepeatLast) { + _lastAction = action + } + val handler = handlerCreator(action) as Handler + handler.invoke(this as HStore, action) + } + + /** + * Updates the state of the screen. + * @param update - function that updates the state + * */ + override fun updateState(update: (S) -> S) { + _state.update(update) + } + + /** + * Sends an event to the screen. The event is sent on the default dispatcher of the AppDispatcher. + * @param event - event to be sent + * @see AppDispatcher + * */ + override fun sendEvent(event: E) { + logger.i("sendEvent: $event") + _event.tryEmit(event).also { isProcess -> + if (isProcess.not()) { + scope.launch { _event.emit(event) } + } + } + } + + /** + * Launches a coroutine and catches exceptions. The coroutine is launched on the default dispatcher of the AppDispatcher. + * @param onError - error handler + * @param onSuccess - success handler + * @param action - action to be executed + * @return Job + * @see Job + * @see AppDispatcher + * */ + override fun launch( + onError: suspend (Throwable) -> Unit, + onSuccess: suspend CoroutineScope.(T) -> Unit, + workDispatcher: CoroutineDispatcher, + eachDispatcher: CoroutineDispatcher, + action: suspend CoroutineScope.() -> T, + ) = scope.launch( + onError = onError, + workDispatcher = workDispatcher, + eachDispatcher = eachDispatcher, + onSuccess = onSuccess, + action = action + ) + + /** + * Launches a flow and collects it in the screenModelScope. The flow is collected on the default dispatcher. of the AppDispatcher. + * @param onError - error handler + * @param each - action for each element of the flow + * @return Job + * @see Flow + * @see Job + * @see AppDispatcher + * */ + override fun Flow.launch( + onError: suspend (cause: Throwable) -> Unit, + workDispatcher: CoroutineDispatcher, + eachDispatcher: CoroutineDispatcher, + each: suspend (T) -> Unit + ): Job = scope.launch( + flow = this, + workDispatcher = workDispatcher, + eachDispatcher = eachDispatcher, + onError = onError, + each = each, + ) + + +} \ No newline at end of file diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/CommonEvents.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/CommonEvents.kt new file mode 100644 index 0000000..ce2c68d --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/CommonEvents.kt @@ -0,0 +1,53 @@ +package com.stslex.atten.core.ui.mvi + +import androidx.compose.material3.SnackbarDuration +import androidx.compose.runtime.Stable +import com.stslex.atten.core.ui.kit.components.snackbar.SnackbarType + +interface CommonEvents { + + @Stable + sealed class Snackbar( + open val message: String, + open val duration: SnackbarDuration, + open val withDismissAction: Boolean, + val action: String, + ) : CommonEvents { + + @Stable + data class Error( + override val message: String, + override val duration: SnackbarDuration = SnackbarDuration.Short, + override val withDismissAction: Boolean = false, + ) : Snackbar( + message = message, + action = SnackbarType.ERROR.label, + duration = duration, + withDismissAction = withDismissAction + ) + + @Stable + data class Success( + override val message: String, + override val duration: SnackbarDuration = SnackbarDuration.Short, + override val withDismissAction: Boolean = false, + ) : Snackbar( + message = message, + action = SnackbarType.SUCCESS.label, + duration = duration, + withDismissAction = withDismissAction + ) + + @Stable + data class Info( + override val message: String, + override val duration: SnackbarDuration = SnackbarDuration.Short, + override val withDismissAction: Boolean = false, + ) : Snackbar( + message = message, + action = SnackbarType.INFO.label, + duration = duration, + withDismissAction = withDismissAction + ) + } +} \ No newline at end of file diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/Feature.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/Feature.kt new file mode 100644 index 0000000..71e7db5 --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/Feature.kt @@ -0,0 +1,22 @@ +package com.stslex.atten.core.ui.mvi + +import androidx.compose.runtime.Composable +import com.stslex.atten.core.ui.mvi.processor.StoreProcessor +import com.stslex.atten.core.ui.navigation.Component +import org.koin.core.component.KoinScopeComponent +import org.koin.core.module.Module + +/** + * Feature is a Koin feature module that provides a StoreProcessor. + * It is responsible for managing the state and actions related to the feature. + * + * @see [StoreProcessor] + * */ +interface Feature, TComponent : Component> : + KoinScopeComponent { + + val module: Module + + @Composable + fun processor(component: TComponent): TProcessor +} \ No newline at end of file diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/NavComponentScreen.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/NavComponentScreen.kt new file mode 100644 index 0000000..dbb0eb8 --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/NavComponentScreen.kt @@ -0,0 +1,21 @@ +package com.stslex.atten.core.ui.mvi + +import androidx.compose.runtime.Composable +import com.stslex.atten.core.ui.mvi.processor.StoreProcessor +import com.stslex.atten.core.ui.navigation.Component +import org.koin.compose.module.rememberKoinModules +import org.koin.core.annotation.KoinExperimentalAPI + +@OptIn(KoinExperimentalAPI::class) +@Suppress("UNCHECKED_CAST") +@Composable +fun , TComponent : Component> NavComponentScreen( + feature: Feature, + component: TComponent, + content: @Composable (TProcessor) -> Unit +) { + rememberKoinModules(unloadModules = true) { + listOf(feature.module) + } + content(feature.processor(component)) +} diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/Router.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/Router.kt new file mode 100644 index 0000000..b9a3468 --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/Router.kt @@ -0,0 +1,6 @@ +package com.stslex.atten.core.ui.mvi + +fun interface Router { + + operator fun invoke(event: E) +} diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/Store.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/Store.kt new file mode 100644 index 0000000..a4bf05d --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/Store.kt @@ -0,0 +1,36 @@ +package com.stslex.atten.core.ui.mvi + +import com.stslex.atten.core.ui.mvi.Store.Action +import com.stslex.atten.core.ui.mvi.Store.Event +import com.stslex.atten.core.ui.mvi.Store.State +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.StateFlow + +interface Store { + + /** Flow of the state of the screen. */ + val state: StateFlow + + /** Flow of events that are sent to the screen. */ + val event: SharedFlow + + /** + * Sends an action to the store. Checks if the action is not the same as the last action. + * If the action is not the same as the last action, the last action is updated. + * The action is then processed. + * @param action - action to be sent + */ + fun consume(action: A) + + interface State + + interface Event + + interface Action { + + interface RepeatLast : Action + + interface Navigation : Action + } +} + diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/StoreExt.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/StoreExt.kt new file mode 100644 index 0000000..c2fd960 --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/StoreExt.kt @@ -0,0 +1,20 @@ +package com.stslex.atten.core.ui.mvi + +import com.stslex.atten.core.ui.mvi.handler.HandlerStore +import com.stslex.atten.core.ui.mvi.Store.Action +import com.stslex.atten.core.ui.mvi.Store.Event +import com.stslex.atten.core.ui.mvi.Store.State +import com.stslex.wizard.core.ui.mvi.v2.Handler + +fun > Handler.invoke( + store: HStore, + action: A +) { + with(store) { + this.invoke(action) + } +} + +inline fun > handler( + crossinline block: HandlerStore.(action: A) -> Unit +) = Handler { block(it) } diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/Handler.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/Handler.kt new file mode 100644 index 0000000..e1986d0 --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/Handler.kt @@ -0,0 +1,10 @@ +package com.stslex.wizard.core.ui.mvi.v2 + +import com.stslex.atten.core.ui.mvi.handler.HandlerStore +import com.stslex.atten.core.ui.mvi.Store.Action + +fun interface Handler> { + + operator fun TStore.invoke(action: A) + +} diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/HandlerCreator.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/HandlerCreator.kt new file mode 100644 index 0000000..eeeb159 --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/HandlerCreator.kt @@ -0,0 +1,11 @@ +package com.stslex.wizard.core.ui.mvi.v2 + +import com.stslex.atten.core.ui.mvi.handler.HandlerStore +import com.stslex.atten.core.ui.mvi.Store.Action +import com.stslex.atten.core.ui.mvi.Store.Event +import com.stslex.atten.core.ui.mvi.Store.State + +fun interface HandlerCreator> { + + operator fun invoke(action: A): Handler<*, HStore> +} \ No newline at end of file diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/HandlerStore.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/HandlerStore.kt new file mode 100644 index 0000000..ba91879 --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/HandlerStore.kt @@ -0,0 +1,73 @@ +package com.stslex.atten.core.ui.mvi.handler + +import com.stslex.atten.core.logger.Logger +import com.stslex.atten.core.coroutine.dispatcher.AppDispatcher +import com.stslex.atten.core.coroutine.scope.AppCoroutineScope +import com.stslex.wizard.core.core.coroutine.AppDispatcherImpl +import com.stslex.atten.core.ui.mvi.Store +import com.stslex.atten.core.ui.mvi.Store.Event +import com.stslex.atten.core.ui.mvi.Store.State +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow + +/** + * A generic interface for managing state, actions, and events within a component or module. It provides a central point for handling state updates, dispatching actions and events, logging, and launching coroutines. + * + * @param S The type of the state managed by the store. Must implement [State]. + * @param A The type of actions that can be dispatched to the store. Must implement [Store.Action]. + * @param E The type of events that the store can emit. Must implement [Event]. + */ +interface HandlerStore { + + val state: StateFlow + + val lastAction: A? + + val logger: Logger + + val appDispatcher: AppDispatcher + + val scope: AppCoroutineScope + + fun sendEvent(event: E) + + fun consume(action: A) + + fun updateState(update: (S) -> S) + + /** + * Launches a coroutine and catches exceptions. The coroutine is launched on the default dispatcher of the AppDispatcher. + * @param onError - error handler + * @param onSuccess - success handler + * @param action - action to be executed + * @return Job + * @see Job + * @see AppDispatcher + * */ + fun launch( + onError: suspend (Throwable) -> Unit = {}, + onSuccess: suspend CoroutineScope.(T) -> Unit = {}, + workDispatcher: CoroutineDispatcher = appDispatcher.default, + eachDispatcher: CoroutineDispatcher = appDispatcher.default, + action: suspend CoroutineScope.() -> T, + ): Job + + /** + * Launches a flow and collects it in the screenModelScope. The flow is collected on the default dispatcher. of the AppDispatcher. + * @param onError - error handler + * @param each - action for each element of the flow + * @return Job + * @see Flow + * @see Job + * @see AppDispatcher + * */ + fun Flow.launch( + onError: suspend (cause: Throwable) -> Unit = {}, + workDispatcher: CoroutineDispatcher = AppDispatcherImpl.default, + eachDispatcher: CoroutineDispatcher = appDispatcher.default, + each: suspend (T) -> Unit + ): Job +} \ No newline at end of file diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/ActionProcessor.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/ActionProcessor.kt new file mode 100644 index 0000000..661d532 --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/ActionProcessor.kt @@ -0,0 +1,14 @@ +package com.stslex.wizard.core.ui.mvi.v2.processor + +import androidx.compose.runtime.Immutable +import com.stslex.atten.core.ui.mvi.Store + +@Immutable +class ActionProcessor>( + val store: TStore, +) { + + operator fun invoke(action: A) { + store.consume(action) + } +} \ No newline at end of file diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/EffectsProcessor.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/EffectsProcessor.kt new file mode 100644 index 0000000..20d03b6 --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/EffectsProcessor.kt @@ -0,0 +1,18 @@ +package com.stslex.wizard.core.ui.mvi.v2.processor + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.LaunchedEffect +import com.stslex.atten.core.ui.mvi.Store +import kotlinx.coroutines.CoroutineScope + +@Immutable +class EffectsProcessor>( + val store: TStore, +) { + + @Composable + operator fun invoke(block: suspend CoroutineScope.(E) -> Unit) { + LaunchedEffect(Unit) { store.event.collect { block(it) } } + } +} \ No newline at end of file diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/StoreProcessor.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/StoreProcessor.kt new file mode 100644 index 0000000..e64f2d2 --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/StoreProcessor.kt @@ -0,0 +1,74 @@ +package com.stslex.atten.core.ui.mvi.processor + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.remember +import com.stslex.atten.core.ui.mvi.Store.Action +import com.stslex.atten.core.ui.mvi.Store.Event +import com.stslex.atten.core.ui.mvi.Store.State +import com.stslex.atten.core.ui.mvi.BaseStore +import com.stslex.atten.core.ui.navigation.Component +import com.stslex.wizard.core.ui.mvi.v2.processor.ActionProcessor +import com.stslex.wizard.core.ui.mvi.v2.processor.EffectsProcessor +import kotlinx.coroutines.CoroutineScope +import org.koin.compose.viewmodel.koinViewModel +import org.koin.core.component.KoinScopeComponent +import org.koin.core.parameter.parametersOf +import androidx.compose.runtime.State as ComposeState + + +/** + * StoreProcessor is an interface that defines the contract for processing actions and events in a store. + * It provides methods to consume actions and handle events. + * + * @param S The type of the state. + * @param A The type of the action. + * @param E The type of the event. + */ +@Immutable +interface StoreProcessor { + + val state: ComposeState + + fun consume(action: A) + + @Composable + fun handle(block: suspend CoroutineScope.(E) -> Unit) +} + +/** + * StoreProcessorImpl is an implementation of the StoreProcessor interface. + * It provides methods to consume actions and handle events in a store. + * + * @param S The type of the state. + * @param A The type of the action. + * @param E The type of the event. + * @param TStoreImpl The type of the store implementation. + */ +@Composable +inline fun , + TComponent : Component + > KoinScopeComponent.rememberStoreProcessor(component: TComponent): StoreProcessor { + val store = koinViewModel( + scope = scope, + qualifier = scope.scopeQualifier, + parameters = { + parametersOf(component) + } + ) + val actionProcessor = remember { ActionProcessor(store) } + val effectsProcessor = remember { EffectsProcessor(store) } + val state = remember { store.state }.collectAsState() + return remember { + StoreProcessorImpl( + actionProcessor = actionProcessor, + eventProcessor = effectsProcessor, + state = state, + ) + } +} + diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/StoreProcessorImpl.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/StoreProcessorImpl.kt new file mode 100644 index 0000000..16c7b9b --- /dev/null +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/StoreProcessorImpl.kt @@ -0,0 +1,31 @@ +package com.stslex.atten.core.ui.mvi.processor + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.State +import com.stslex.atten.core.ui.mvi.Store +import com.stslex.wizard.core.ui.mvi.v2.processor.ActionProcessor +import com.stslex.wizard.core.ui.mvi.v2.processor.EffectsProcessor +import kotlinx.coroutines.CoroutineScope + +@Immutable +class StoreProcessorImpl< + S : Store.State, + A : Store.Action, + E : Store.Event, + TStore : Store, + >( + private val actionProcessor: ActionProcessor, + private val eventProcessor: EffectsProcessor, + override val state: State, +) : StoreProcessor { + + override fun consume(action: A) { + actionProcessor(action) + } + + @Composable + override fun handle(block: suspend CoroutineScope.(E) -> Unit) { + eventProcessor(block) + } +} \ No newline at end of file diff --git a/core/ui/navigation/build.gradle.kts b/core/ui/navigation/build.gradle.kts index ec77178..82d834a 100644 --- a/core/ui/navigation/build.gradle.kts +++ b/core/ui/navigation/build.gradle.kts @@ -1,11 +1,10 @@ plugins { alias(libs.plugins.convention.kmp.library.compose) + alias(libs.plugins.convention.kmp.navigation) } kotlin { sourceSets.commonMain.dependencies { implementation(project(":core:core")) - - api(libs.compose.navigation) } } \ No newline at end of file diff --git a/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Component.kt b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Component.kt new file mode 100644 index 0000000..cd0f1ae --- /dev/null +++ b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Component.kt @@ -0,0 +1,7 @@ +package com.stslex.atten.core.ui.navigation + +import androidx.compose.runtime.Stable +import com.arkivanov.decompose.ComponentContext + +@Stable +interface Component : ComponentContext diff --git a/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Config.kt b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Config.kt new file mode 100644 index 0000000..04f17b8 --- /dev/null +++ b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Config.kt @@ -0,0 +1,18 @@ +package com.stslex.atten.core.ui.navigation + +import androidx.compose.runtime.Stable +import kotlinx.serialization.Serializable + +@Serializable +@Stable +sealed interface Config { + + val isBackAllow: Boolean + get() = true + + data object Home : Config + + data class Detail( + val id: String + ) : Config +} \ No newline at end of file diff --git a/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavExt.kt b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavExt.kt deleted file mode 100644 index 17667e0..0000000 --- a/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavExt.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.stslex.atten.core.ui.navigation - -import androidx.compose.runtime.Composable -import androidx.navigation.NavGraphBuilder -import androidx.navigation.compose.composable -import androidx.navigation.toRoute - -inline fun NavGraphBuilder.navScreen( - noinline content: @Composable (S) -> Unit -) { - composable { backStackEntry -> - content(backStackEntry.toRoute()) - } -} diff --git a/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Navigator.kt b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Navigator.kt deleted file mode 100644 index bd8b8ea..0000000 --- a/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Navigator.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.stslex.atten.core.ui.navigation - -interface Navigator { - - fun navigateTo( - screen: Screen, - options: NavigatorOptions = NavigatorOptions() - ) - - fun popBack() -} diff --git a/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavigatorImpl.kt b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavigatorImpl.kt deleted file mode 100644 index c513b93..0000000 --- a/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavigatorImpl.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.stslex.atten.core.ui.navigation - -import androidx.navigation.NavHostController -import com.stslex.atten.core.Logger - -class NavigatorImpl( - private val navHostController: NavHostController -) : Navigator { - - override fun navigateTo(screen: Screen, options: NavigatorOptions) { - Logger.d("navigateTo screen: $screen, options: $options", TAG) - val currentRoute = navHostController.currentDestination?.route ?: return - try { - navHostController.navigate(screen) { - if (options.isSingleTop.not()) return@navigate - - popUpTo(currentRoute) { - inclusive = true - saveState = true - } - launchSingleTop = true - } - } catch (exception: Exception) { - Logger.e(exception, TAG, "screen: $screen") - } - } - - override fun popBack() { - Logger.d("process popBackStack", TAG) - navHostController.popBackStack() - } - - companion object { - private const val TAG = "NAVIGATION" - } -} \ No newline at end of file diff --git a/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavigatorOptions.kt b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavigatorOptions.kt deleted file mode 100644 index 18b114b..0000000 --- a/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/NavigatorOptions.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.stslex.atten.core.ui.navigation - -data class NavigatorOptions( - val isSingleTop: Boolean = false, -) diff --git a/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Screen.kt b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Screen.kt deleted file mode 100644 index e672b9b..0000000 --- a/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Screen.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.stslex.atten.core.ui.navigation - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -sealed interface Screen { - - @Serializable - data object Home : Screen - - @Serializable - data class DetailScreen( - @SerialName("id") val id: String - ) : Screen -} diff --git a/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Target.kt b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Target.kt new file mode 100644 index 0000000..b0173cd --- /dev/null +++ b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/Target.kt @@ -0,0 +1,3 @@ +package com.stslex.wizard.core.navigation + +interface Target \ No newline at end of file diff --git a/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/di/ModuleCoreNavigation.kt b/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/di/ModuleCoreNavigation.kt deleted file mode 100644 index 4a0eab1..0000000 --- a/core/ui/navigation/src/commonMain/kotlin/com/stslex/atten/core/ui/navigation/di/ModuleCoreNavigation.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.stslex.atten.core.ui.navigation.di - -import androidx.navigation.NavHostController -import com.stslex.atten.core.di.AppModule -import com.stslex.atten.core.ui.navigation.Navigator -import com.stslex.atten.core.ui.navigation.NavigatorImpl -import org.koin.core.annotation.Module -import org.koin.dsl.module -import org.koin.core.module.Module as KoinModule - -@Module -class ModuleCoreNavigation(navController: NavHostController) : AppModule { - - override val module: KoinModule = module { - single { NavigatorImpl(navController) } - } -} \ No newline at end of file diff --git a/feature/details/build.gradle.kts b/feature/details/build.gradle.kts index f4280fe..1b5ea94 100644 --- a/feature/details/build.gradle.kts +++ b/feature/details/build.gradle.kts @@ -1,12 +1,12 @@ plugins { - alias(libs.plugins.convention.kmp.library.compose) - alias(libs.plugins.convention.android.library.compose) + alias(libs.plugins.convention.kmp.feature) } kotlin { sourceSets.commonMain.dependencies { implementation(project(":core:core")) implementation(project(":core:ui:kit")) + implementation(project(":core:ui:mvi")) implementation(project(":core:ui:navigation")) implementation(project(":core:database")) implementation(project(":core:todo")) diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt index e6b2b4c..2ce12e1 100644 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt @@ -1,23 +1,22 @@ package com.stslex.atten.feature.details.di -import com.stslex.atten.core.di.AppModule import com.stslex.atten.core.ui.kit.mvi.storeOf import com.stslex.atten.feature.details.domain.interactor.DetailsInteractor import com.stslex.atten.feature.details.domain.interactor.DetailsInteractorImpl -import com.stslex.atten.feature.details.navigation.DetailsRouter -import com.stslex.atten.feature.details.navigation.DetailsRouterImpl import com.stslex.atten.feature.details.ui.store.DetailsStore +import org.koin.core.annotation.ComponentScan import org.koin.core.annotation.Module import org.koin.core.module.dsl.bind import org.koin.core.module.dsl.factoryOf import org.koin.dsl.module @Module -class FeatureDetailsModule : AppModule { +@ComponentScan("com.stslex.atten.feature.details") +class FeatureDetailsModule { - override val module = module { + val module = module { factoryOf(::DetailsInteractorImpl) { bind() } - factoryOf(::DetailsRouterImpl) { bind() } +// factoryOf(::DetailsRouterImpl) { bind() } storeOf(::DetailsStore) } } diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsGraph.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsGraph.kt index 17fc891..7697114 100644 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsGraph.kt +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsGraph.kt @@ -5,31 +5,31 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.navigation.NavGraphBuilder -import com.stslex.atten.core.ui.navigation.Screen +import com.stslex.atten.core.ui.navigation.Component import com.stslex.atten.core.ui.navigation.navScreen import com.stslex.atten.core.ui.kit.mvi.getStore import com.stslex.atten.feature.details.ui.DetailsScreen import com.stslex.atten.feature.details.ui.store.DetailsStore import com.stslex.atten.feature.details.ui.store.DetailsStoreComponent.Action -fun NavGraphBuilder.detailsGraph() { - navScreen { args -> - val store = getStore() - - DisposableEffect(Unit) { - store.dispatch(Action.Init(args.id)) - onDispose { - store.dispatch(Action.OnScreenLeft) - } - } - - val state by remember(store) { store.state }.collectAsState() - - DetailsScreen( - state = state, - onTitleChange = { store.dispatch(Action.OnTitleValueChanged(it)) }, - onDescriptionChange = { store.dispatch(Action.OnDescriptionValueChanged(it)) }, - onSaveClicked = { store.dispatch(Action.OnSaveClicked) } - ) - } -} +//fun NavGraphBuilder.detailsGraph() { +// navScreen { args -> +// val store = getStore() +// +// DisposableEffect(Unit) { +// store.dispatch(Action.Init(args.id)) +// onDispose { +// store.dispatch(Action.OnScreenLeft) +// } +// } +// +// val state by remember(store) { store.state }.collectAsState() +// +// DetailsScreen( +// state = state, +// onTitleChange = { store.dispatch(Action.OnTitleValueChanged(it)) }, +// onDescriptionChange = { store.dispatch(Action.OnDescriptionValueChanged(it)) }, +// onSaveClicked = { store.dispatch(Action.OnSaveClicked) } +// ) +// } +//} diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsRouter.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsRouter.kt index 23ba0e4..33752c9 100644 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsRouter.kt +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsRouter.kt @@ -1,14 +1,14 @@ package com.stslex.atten.feature.details.navigation -import com.stslex.atten.core.ui.navigation.Navigator import com.stslex.atten.core.ui.kit.mvi.Router +import com.stslex.atten.feature.details.ui.mvi.DetailsComponent import com.stslex.atten.feature.details.ui.store.DetailsStoreComponent.Navigation interface DetailsRouter : Router -class DetailsRouterImpl( - private val navigator: Navigator -) : DetailsRouter { +class DetailsComponentImpl( + private val popBack: () -> Unit, +) : DetailsComponent { override fun invoke(event: Navigation) { when (event) { diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsComponent.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsComponent.kt new file mode 100644 index 0000000..0b668a6 --- /dev/null +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsComponent.kt @@ -0,0 +1,5 @@ +package com.stslex.atten.feature.details.ui.mvi + +import com.stslex.atten.core.ui.navigation.Component + +interface DetailsComponent : Component \ No newline at end of file diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStore.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStore.kt index 4032f57..adea887 100644 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStore.kt +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStore.kt @@ -1,6 +1,6 @@ package com.stslex.atten.feature.details.ui.store -import com.stslex.atten.core.Logger +import com.stslex.atten.core.logger.Logger import com.stslex.atten.core.coroutine.dispatcher.AppDispatcher import com.stslex.atten.core.ui.kit.mvi.Store import com.stslex.atten.core.ui.kit.mvi.StoreComponent.Event.Snackbar diff --git a/feature/home/build.gradle.kts b/feature/home/build.gradle.kts index 8fcc1a1..f903b1a 100644 --- a/feature/home/build.gradle.kts +++ b/feature/home/build.gradle.kts @@ -1,12 +1,12 @@ plugins { - alias(libs.plugins.convention.kmp.library.compose) - alias(libs.plugins.convention.android.library.compose) + alias(libs.plugins.convention.kmp.feature) } kotlin { sourceSets.commonMain.dependencies { implementation(project(":core:core")) implementation(project(":core:ui:kit")) + implementation(project(":core:ui:mvi")) implementation(project(":core:ui:navigation")) implementation(project(":core:todo")) implementation(project(":core:paging")) diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt index c2e62a9..15285aa 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt @@ -1,23 +1,23 @@ package com.stslex.atten.feature.home.di -import com.stslex.atten.core.di.AppModule import com.stslex.atten.core.ui.kit.mvi.storeOf import com.stslex.atten.feature.home.domain.interactor.HomeScreenInteractor import com.stslex.atten.feature.home.domain.interactor.HomeScreenInteractorImpl -import com.stslex.atten.feature.home.navigation.HomeRouter -import com.stslex.atten.feature.home.navigation.HomeRouterImpl import com.stslex.atten.feature.home.ui.store.HomeStore +import org.koin.core.annotation.ComponentScan import org.koin.core.annotation.Module import org.koin.core.module.dsl.bind import org.koin.core.module.dsl.factoryOf import org.koin.dsl.module + @Module -class ModuleFeatureHome : AppModule { +@ComponentScan("com.stslex.atten.feature.home") +class ModuleFeatureHome { - override val module = module { + val module = module { factoryOf(::HomeScreenInteractorImpl) { bind() } - factoryOf(::HomeRouterImpl) { bind() } +// factoryOf(::HomeRouterImpl) { bind() } storeOf(::HomeStore) } } diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeGraph.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeGraph.kt index 0efffcd..221fb68 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeGraph.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeGraph.kt @@ -7,7 +7,7 @@ import androidx.compose.runtime.remember import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalHapticFeedback import androidx.navigation.NavGraphBuilder -import com.stslex.atten.core.ui.navigation.Screen +import com.stslex.atten.core.ui.navigation.Component import com.stslex.atten.core.ui.navigation.navScreen import com.stslex.atten.core.ui.kit.mvi.getStore import com.stslex.atten.feature.home.ui.HomeScreen @@ -17,7 +17,7 @@ import com.stslex.atten.feature.home.ui.store.HomeStoreComponent.Event import kotlinx.coroutines.flow.collectLatest fun NavGraphBuilder.homeGraph() { - navScreen { + navScreen { val store = getStore() val state by remember { store.state }.collectAsState() val hapticFeedback = LocalHapticFeedback.current diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouterImpl.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouterImpl.kt index 118c73b..d9c0371 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouterImpl.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouterImpl.kt @@ -1,6 +1,6 @@ package com.stslex.atten.feature.home.navigation -import com.stslex.atten.core.ui.navigation.Screen +import com.stslex.atten.core.ui.navigation.Component import com.stslex.atten.core.ui.navigation.Navigator import com.stslex.atten.feature.home.ui.store.HomeStoreComponent.Navigation @@ -10,7 +10,7 @@ class HomeRouterImpl( override fun invoke(event: Navigation) { when (event) { - is Navigation.NavigateToDetail -> navigator.navigateTo(Screen.DetailScreen(event.id)) + is Navigation.NavigateToDetail -> navigator.navigateTo(Component.DetailScreen(event.id)) is Navigation.Back -> navigator.popBack() } } diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeComponent.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeComponent.kt new file mode 100644 index 0000000..552cf6f --- /dev/null +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeComponent.kt @@ -0,0 +1,5 @@ +package com.stslex.atten.feature.home.ui.mvi + +import com.stslex.atten.core.ui.navigation.Component + +interface HomeComponent : Component \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 946b17e..5e91bdb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,6 +30,11 @@ serialization = "1.7.1" navigation = "2.9.0-beta03" datetime = "0.6.0" +# decompose dependencies +decompose = "3.3.0" +essenty = "2.5.0" +parcelize = "0.2.4" + [libraries] android-gradlePlugin = { module = "com.android.tools.build:gradle", version.ref = "agp" } kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } @@ -45,6 +50,8 @@ compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "android-compose" } compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "android-compose" } compose-material3 = { module = "androidx.compose.material3:material3", version.ref = "compose-material3" } + +# todo check maybe we can replace decompose with compose-navigation compose-navigation = { module = "org.jetbrains.androidx.navigation:navigation-compose", version.ref = "navigation" } coroutine-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutine" } @@ -53,9 +60,11 @@ coroutine-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutine koin-core = { group = "io.insert-koin", name = "koin-core", version.ref = "koin" } koin-android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin" } koin-android-compose = { group = "io.insert-koin", name = "koin-androidx-compose", version.ref = "koin" } -koin-ksp-compiler = { group = "io.insert-koin", name = "koin-ksp-compiler", version.ref = "koin-ksp" } -koin-annotations = { group = "io.insert-koin", name = "koin-annotations", version.ref = "koin-ksp" } koin-compose = { group = "io.insert-koin", name = "koin-compose", version.ref = "koin" } +koin-compose-viewmodel = { group = "io.insert-koin", name = "koin-compose-viewmodel", version.ref = "koin" } + +koin-ksp-compiler = { group = "io.insert-koin", name = "koin-ksp-compiler", version.ref = "koin-ksp" } +koin-ksp-annotations = { group = "io.insert-koin", name = "koin-annotations", version.ref = "koin-ksp" } koin-test = { group = "io.insert-koin", name = "koin-test" } koin-test-junit = { group = "io.insert-koin", name = "koin-test-junit4" } @@ -76,6 +85,13 @@ sqlite = { module = "androidx.sqlite:sqlite", version.ref = "sqlite" } kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "serialization" } kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "datetime" } + +decompose = { module = "com.arkivanov.decompose:decompose", version.ref = "decompose" } +decompose-extensions = { module = "com.arkivanov.decompose:extensions-compose", version.ref = "decompose" } +essenty-lifecycle = { module = "com.arkivanov.essenty:lifecycle", version.ref = "essenty" } +essenty-stateKeeper = { module = "com.arkivanov.essenty:state-keeper", version.ref = "essenty" } +essenty-backHandler = { module = "com.arkivanov.essenty:back-handler", version.ref = "essenty" } +parcelize-darwin = { module = "com.arkivanov.parcelize.darwin:runtime", version.ref = "parcelize" } [bundles] test = [ "kotlin-test", @@ -95,11 +111,13 @@ kotlinCocoapods = { id = "org.jetbrains.kotlin.native.cocoapods", version.ref = jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" } composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } -convention-kmp-library = { id = "convention.kmp.library", version = "1.0" } -convention-kmp-library-compose = { id = "convention.kmp.library.compose", version = "1.0" } -convention-kmp-application = { id = "convention.kmp.application", version = "1.0" } -convention-android-library-compose = { id = "convention.android.library.compose", version = "1.0" } -convention-kmp-library-room = { id = "convention.kmp.library.room", version = "1.0" } +convention-kmp-library = { id = "convention.kmp.library" } +convention-kmp-library-compose = { id = "convention.kmp.library.compose" } +convention-kmp-application = { id = "convention.kmp.application" } +convention-android-library-compose = { id = "convention.android.library.compose" } +convention-kmp-room = { id = "convention.kmp.room" } +convention-kmp-navigation = { id = "convention.kmp.navigation" } +convention-kmp-feature = { id = "convention.kmp.feature" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } room = { id = "androidx.room", version.ref = "room" } diff --git a/iosApp/iosApp/iOSApp.swift b/iosApp/iosApp/iOSApp.swift index 0648e86..d4cd3c9 100644 --- a/iosApp/iosApp/iOSApp.swift +++ b/iosApp/iosApp/iOSApp.swift @@ -1,7 +1,13 @@ import SwiftUI +import commonApp @main struct iOSApp: App { + + init() { + InitKoinKt.InitKoin() + } + var body: some Scene { WindowGroup { ContentView() From e67add505883beeb5b6f72041c3988ea0ad7bcc5 Mon Sep 17 00:00:00 2001 From: stslex Date: Fri, 27 Jun 2025 08:43:50 +0300 Subject: [PATCH 3/8] ksp module for koin --- .../atten/convention/KotlinMultiplatform.kt | 5 + .../kotlin/com/stslex/atten/di/AppModules.kt | 14 +-- .../kotlin/com/stslex/atten/ui/InitialApp.kt | 29 ------ .../{ => core}/common/CommonExt.android.kt | 2 +- .../atten/core/{ => core}/common/CommonExt.kt | 2 +- .../core/{ => core}/coroutine/CoroutineExt.kt | 4 +- .../coroutine/dispatcher/AppDispatcher.kt | 2 +- .../coroutine/dispatcher/AppDispatcherImpl.kt | 6 +- .../coroutine/scope/AppCoroutineScope.kt | 6 +- .../stslex/atten/core/core/di/ModuleCore.kt | 8 ++ .../{ => core}/logger/AppLoggerCreator.kt | 2 +- .../atten/core/{ => core}/logger/Log.kt | 6 +- .../atten/core/{ => core}/logger/Logger.kt | 2 +- .../com/stslex/atten/core/di/ModuleCore.kt | 8 -- .../core/{ => core}/common/CommonExt.ios.kt | 2 +- .../core/database/di/ModuleCoreDatabase.kt | 16 ++- .../atten/core/database/model/ToDoEntity.kt | 2 +- .../factory/PagerFactory.kt | 2 +- .../factory/PagerFactoryImpl.kt | 2 +- .../holder/ItemHolderImpl.kt | 2 +- .../model/PagingResponse.kt | 2 +- .../pager/PagerImpl.kt | 4 +- .../states/PagingState.kt | 2 +- .../worker/PagingWorkerImpl.kt | 2 +- .../data/ToDoItemHolder.kt | 8 ++ .../data/repository/ToDoRepositoryImpl.kt | 14 +-- .../di/ModuleCoreToDo.kt | 16 +-- .../com/stslex/atten/core/ui/kit/mvi/Store.kt | 6 +- .../kit/mvi/StoreExt.ios.kt | 2 +- .../com/stslex/atten/core/ui/mvi/BaseStore.kt | 6 +- .../atten/core/ui/mvi/handler/HandlerStore.kt | 9 +- .../di/FeatureDetailsModule.kt | 6 +- .../navigation/DetailsGraph.kt | 12 --- .../navigation/DetailsRouter.kt | 14 +-- .../ui/store/DetailsStore.kt | 6 +- .../di/ModuleFeatureHome.kt | 6 +- .../navigation/HomeGraph.kt | 98 ++++++++----------- .../navigation/HomeRouterImpl.kt | 17 ---- .../ui/mvi/HomeRouterImpl.kt | 24 +++++ .../ui/store/HomeStore.kt | 2 +- 40 files changed, 162 insertions(+), 216 deletions(-) delete mode 100644 commonApp/src/commonMain/kotlin/com/stslex/atten/ui/InitialApp.kt rename core/core/src/androidMain/kotlin/com/stslex/atten/core/{ => core}/common/CommonExt.android.kt (82%) rename core/core/src/commonMain/kotlin/com/stslex/atten/core/{ => core}/common/CommonExt.kt (58%) rename core/core/src/commonMain/kotlin/com/stslex/atten/core/{ => core}/coroutine/CoroutineExt.kt (82%) rename core/core/src/commonMain/kotlin/com/stslex/atten/core/{ => core}/coroutine/dispatcher/AppDispatcher.kt (83%) rename core/core/src/commonMain/kotlin/com/stslex/atten/core/{ => core}/coroutine/dispatcher/AppDispatcherImpl.kt (82%) rename core/core/src/commonMain/kotlin/com/stslex/atten/core/{ => core}/coroutine/scope/AppCoroutineScope.kt (94%) create mode 100644 core/core/src/commonMain/kotlin/com/stslex/atten/core/core/di/ModuleCore.kt rename core/core/src/commonMain/kotlin/com/stslex/atten/core/{ => core}/logger/AppLoggerCreator.kt (94%) rename core/core/src/commonMain/kotlin/com/stslex/atten/core/{ => core}/logger/Log.kt (91%) rename core/core/src/commonMain/kotlin/com/stslex/atten/core/{ => core}/logger/Logger.kt (81%) delete mode 100644 core/core/src/commonMain/kotlin/com/stslex/atten/core/di/ModuleCore.kt rename core/core/src/iosMain/kotlin/com/stslex/atten/core/{ => core}/common/CommonExt.ios.kt (85%) create mode 100644 core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/data/ToDoItemHolder.kt delete mode 100644 feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouterImpl.kt create mode 100644 feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeRouterImpl.kt diff --git a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinMultiplatform.kt b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinMultiplatform.kt index 22a922d..4963b3f 100644 --- a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinMultiplatform.kt +++ b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinMultiplatform.kt @@ -7,6 +7,7 @@ import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile internal fun Project.configureKotlinMultiplatform( ) = extensions.configure { @@ -56,4 +57,8 @@ internal fun Project.configureKotlinMultiplatform( //applying the Cocoapods Configuration we made (this as ExtensionAware).extensions.configure(::configureKotlinCocoapods) + +// tasks.withType(KotlinCompile::class.java).all { +// if (name != "kspCommonMainKotlinMetadata") dependsOn("kspCommonMainKotlinMetadata") +// } } \ No newline at end of file diff --git a/commonApp/src/commonMain/kotlin/com/stslex/atten/di/AppModules.kt b/commonApp/src/commonMain/kotlin/com/stslex/atten/di/AppModules.kt index c977113..33b43ff 100644 --- a/commonApp/src/commonMain/kotlin/com/stslex/atten/di/AppModules.kt +++ b/commonApp/src/commonMain/kotlin/com/stslex/atten/di/AppModules.kt @@ -1,27 +1,19 @@ package com.stslex.atten.di +import com.stslex.atten.core.core.di.ModuleCore import com.stslex.atten.core.database.di.ModuleCoreDatabase -import com.stslex.atten.core.di.ModuleCore import com.stslex.atten.core.paging.di.ModuleCorePaging import com.stslex.atten.core.todo.di.ModuleCoreToDo import com.stslex.atten.feature.details.di.FeatureDetailsModule import com.stslex.atten.feature.home.di.ModuleFeatureHome import org.koin.core.module.Module +import org.koin.ksp.generated.module val appModules: List = listOf( - ModuleCore().module, - ModuleCoreDatabase(), - ModuleCoreToDo(), - ModuleCorePaging(), - ModuleFeatureHome(), - FeatureDetailsModule(), -).map { it.module } - -val refactor = listOf( ModuleCore().module, ModuleCoreDatabase().module, ModuleCoreToDo().module, ModuleCorePaging().module, ModuleFeatureHome().module, FeatureDetailsModule().module, -) \ No newline at end of file +) diff --git a/commonApp/src/commonMain/kotlin/com/stslex/atten/ui/InitialApp.kt b/commonApp/src/commonMain/kotlin/com/stslex/atten/ui/InitialApp.kt deleted file mode 100644 index b8502a9..0000000 --- a/commonApp/src/commonMain/kotlin/com/stslex/atten/ui/InitialApp.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.stslex.atten.ui - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost -import com.stslex.atten.core.ui.navigation.Component -import com.stslex.atten.feature.details.navigation.detailsGraph -import com.stslex.atten.feature.home.navigation.homeGraph - -@Composable -internal fun InitialApp( - navHostController: NavHostController, - modifier: Modifier = Modifier, -) { - Box( - modifier = modifier.fillMaxSize() - ) { - NavHost( - navController = navHostController, - startDestination = Component.Home - ) { - homeGraph() - detailsGraph() - } - } -} \ No newline at end of file diff --git a/core/core/src/androidMain/kotlin/com/stslex/atten/core/common/CommonExt.android.kt b/core/core/src/androidMain/kotlin/com/stslex/atten/core/core/common/CommonExt.android.kt similarity index 82% rename from core/core/src/androidMain/kotlin/com/stslex/atten/core/common/CommonExt.android.kt rename to core/core/src/androidMain/kotlin/com/stslex/atten/core/core/common/CommonExt.android.kt index 24e35bc..eb529dd 100644 --- a/core/core/src/androidMain/kotlin/com/stslex/atten/core/common/CommonExt.android.kt +++ b/core/core/src/androidMain/kotlin/com/stslex/atten/core/core/common/CommonExt.android.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.common +package com.stslex.atten.core.core.common import com.stslex.atten.core.core.BuildConfig import java.util.UUID diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/common/CommonExt.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/core/common/CommonExt.kt similarity index 58% rename from core/core/src/commonMain/kotlin/com/stslex/atten/core/common/CommonExt.kt rename to core/core/src/commonMain/kotlin/com/stslex/atten/core/core/common/CommonExt.kt index 55be590..94e09d9 100644 --- a/core/core/src/commonMain/kotlin/com/stslex/atten/core/common/CommonExt.kt +++ b/core/core/src/commonMain/kotlin/com/stslex/atten/core/core/common/CommonExt.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.common +package com.stslex.atten.core.core.common expect val randomUuid: String diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/CoroutineExt.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/core/coroutine/CoroutineExt.kt similarity index 82% rename from core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/CoroutineExt.kt rename to core/core/src/commonMain/kotlin/com/stslex/atten/core/core/coroutine/CoroutineExt.kt index 249a6e2..86d931b 100644 --- a/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/CoroutineExt.kt +++ b/core/core/src/commonMain/kotlin/com/stslex/atten/core/core/coroutine/CoroutineExt.kt @@ -1,6 +1,6 @@ -package com.stslex.atten.core.coroutine +package com.stslex.atten.core.core.coroutine -import com.stslex.atten.core.logger.Log +import com.stslex.atten.core.core.logger.Log import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/dispatcher/AppDispatcher.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/core/coroutine/dispatcher/AppDispatcher.kt similarity index 83% rename from core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/dispatcher/AppDispatcher.kt rename to core/core/src/commonMain/kotlin/com/stslex/atten/core/core/coroutine/dispatcher/AppDispatcher.kt index 4707e60..2dec53a 100644 --- a/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/dispatcher/AppDispatcher.kt +++ b/core/core/src/commonMain/kotlin/com/stslex/atten/core/core/coroutine/dispatcher/AppDispatcher.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.coroutine.dispatcher +package com.stslex.atten.core.core.coroutine.dispatcher import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.MainCoroutineDispatcher diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/dispatcher/AppDispatcherImpl.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/core/coroutine/dispatcher/AppDispatcherImpl.kt similarity index 82% rename from core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/dispatcher/AppDispatcherImpl.kt rename to core/core/src/commonMain/kotlin/com/stslex/atten/core/core/coroutine/dispatcher/AppDispatcherImpl.kt index 9e889ec..db32744 100644 --- a/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/dispatcher/AppDispatcherImpl.kt +++ b/core/core/src/commonMain/kotlin/com/stslex/atten/core/core/coroutine/dispatcher/AppDispatcherImpl.kt @@ -1,12 +1,12 @@ -package com.stslex.atten.core.coroutine.dispatcher +package com.stslex.atten.core.core.coroutine.dispatcher import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.IO import kotlinx.coroutines.MainCoroutineDispatcher -import org.koin.core.annotation.Singleton +import org.koin.core.annotation.Single -@Singleton +@Single class AppDispatcherImpl : AppDispatcher { override val default: CoroutineDispatcher = Dispatchers.Default override val io: CoroutineDispatcher = Dispatchers.IO diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/scope/AppCoroutineScope.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/core/coroutine/scope/AppCoroutineScope.kt similarity index 94% rename from core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/scope/AppCoroutineScope.kt rename to core/core/src/commonMain/kotlin/com/stslex/atten/core/core/coroutine/scope/AppCoroutineScope.kt index f911331..474162e 100644 --- a/core/core/src/commonMain/kotlin/com/stslex/atten/core/coroutine/scope/AppCoroutineScope.kt +++ b/core/core/src/commonMain/kotlin/com/stslex/atten/core/core/coroutine/scope/AppCoroutineScope.kt @@ -1,7 +1,7 @@ -package com.stslex.atten.core.coroutine.scope +package com.stslex.atten.core.core.coroutine.scope -import com.stslex.atten.core.coroutine.dispatcher.AppDispatcher -import com.stslex.atten.core.logger.Log +import com.stslex.atten.core.core.coroutine.dispatcher.AppDispatcher +import com.stslex.atten.core.core.logger.Log import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/core/di/ModuleCore.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/core/di/ModuleCore.kt new file mode 100644 index 0000000..11c1d71 --- /dev/null +++ b/core/core/src/commonMain/kotlin/com/stslex/atten/core/core/di/ModuleCore.kt @@ -0,0 +1,8 @@ +package com.stslex.atten.core.core.di + +import org.koin.core.annotation.ComponentScan +import org.koin.core.annotation.Module + +@Module +@ComponentScan("com.stslex.atten.core.core") +class ModuleCore diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/AppLoggerCreator.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/core/logger/AppLoggerCreator.kt similarity index 94% rename from core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/AppLoggerCreator.kt rename to core/core/src/commonMain/kotlin/com/stslex/atten/core/core/logger/AppLoggerCreator.kt index 556a058..7e0460d 100644 --- a/core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/AppLoggerCreator.kt +++ b/core/core/src/commonMain/kotlin/com/stslex/atten/core/core/logger/AppLoggerCreator.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.logger +package com.stslex.atten.core.core.logger internal class AppLoggerCreator( private val tag: String diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/Log.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/core/logger/Log.kt similarity index 91% rename from core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/Log.kt rename to core/core/src/commonMain/kotlin/com/stslex/atten/core/core/logger/Log.kt index 5981302..c4aefac 100644 --- a/core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/Log.kt +++ b/core/core/src/commonMain/kotlin/com/stslex/atten/core/core/logger/Log.kt @@ -1,8 +1,8 @@ -package com.stslex.atten.core.logger +package com.stslex.atten.core.core.logger import co.touchlab.kermit.Logger -import com.stslex.atten.core.common.isDebug -import com.stslex.atten.core.logger.Logger as AtTenLogger +import com.stslex.atten.core.core.common.isDebug +import com.stslex.atten.core.core.logger.Logger as AtTenLogger object Log : AtTenLogger { diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/Logger.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/core/logger/Logger.kt similarity index 81% rename from core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/Logger.kt rename to core/core/src/commonMain/kotlin/com/stslex/atten/core/core/logger/Logger.kt index 180cd42..1f6ed0a 100644 --- a/core/core/src/commonMain/kotlin/com/stslex/atten/core/logger/Logger.kt +++ b/core/core/src/commonMain/kotlin/com/stslex/atten/core/core/logger/Logger.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.logger +package com.stslex.atten.core.core.logger interface Logger { diff --git a/core/core/src/commonMain/kotlin/com/stslex/atten/core/di/ModuleCore.kt b/core/core/src/commonMain/kotlin/com/stslex/atten/core/di/ModuleCore.kt deleted file mode 100644 index ced4145..0000000 --- a/core/core/src/commonMain/kotlin/com/stslex/atten/core/di/ModuleCore.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.stslex.atten.core.di - -import org.koin.core.annotation.ComponentScan -import org.koin.core.annotation.Module - -@Module -@ComponentScan("com.stslex.atten.core") -class ModuleCore \ No newline at end of file diff --git a/core/core/src/iosMain/kotlin/com/stslex/atten/core/common/CommonExt.ios.kt b/core/core/src/iosMain/kotlin/com/stslex/atten/core/core/common/CommonExt.ios.kt similarity index 85% rename from core/core/src/iosMain/kotlin/com/stslex/atten/core/common/CommonExt.ios.kt rename to core/core/src/iosMain/kotlin/com/stslex/atten/core/core/common/CommonExt.ios.kt index 2ab69ac..dcc2408 100644 --- a/core/core/src/iosMain/kotlin/com/stslex/atten/core/common/CommonExt.ios.kt +++ b/core/core/src/iosMain/kotlin/com/stslex/atten/core/core/common/CommonExt.ios.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.core.common +package com.stslex.atten.core.core.common import platform.Foundation.NSUUID import kotlin.experimental.ExperimentalNativeApi diff --git a/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/di/ModuleCoreDatabase.kt b/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/di/ModuleCoreDatabase.kt index 9d28bf7..2112539 100644 --- a/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/di/ModuleCoreDatabase.kt +++ b/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/di/ModuleCoreDatabase.kt @@ -4,17 +4,23 @@ import com.stslex.atten.core.database.db.AppDatabase import com.stslex.atten.core.database.db.ToDoDao import org.koin.core.annotation.ComponentScan import org.koin.core.annotation.Module +import org.koin.core.annotation.Single import org.koin.core.scope.Scope -import org.koin.dsl.module @Module @ComponentScan("com.stslex.atten.core.database") class ModuleCoreDatabase { - val module = module { - single { getDatabase() } - single { get().getTodoDao() } - } +// val module = module { +// single { getDatabase() } +// single { get().getTodoDao() } +// } + + @Single + fun appDatabase(scope: Scope): AppDatabase = scope.getDatabase() + + @Single + fun todoDao(scope: Scope): ToDoDao = scope.get().getTodoDao() } internal expect fun Scope.getDatabase(): AppDatabase \ No newline at end of file diff --git a/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/model/ToDoEntity.kt b/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/model/ToDoEntity.kt index 9674aaa..3339018 100644 --- a/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/model/ToDoEntity.kt +++ b/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/model/ToDoEntity.kt @@ -3,7 +3,7 @@ package com.stslex.atten.core.database.model import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey -import com.stslex.atten.core.common.randomUuid +import com.stslex.atten.core.core.common.randomUuid @Entity data class ToDoEntity( diff --git a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/factory/PagerFactory.kt b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/factory/PagerFactory.kt index ca2dc56..cfeab58 100644 --- a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/factory/PagerFactory.kt +++ b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/factory/PagerFactory.kt @@ -1,6 +1,6 @@ package com.stslex.atten.core.paging.factory -import com.stslex.atten.core.coroutine.scope.AppCoroutineScope +import com.stslex.atten.core.core.coroutine.scope.AppCoroutineScope import com.stslex.atten.core.paging.model.PagingItem import com.stslex.atten.core.paging.model.PagingResponse import com.stslex.atten.core.paging.holder.ItemHolder diff --git a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/factory/PagerFactoryImpl.kt b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/factory/PagerFactoryImpl.kt index 7841458..429cf98 100644 --- a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/factory/PagerFactoryImpl.kt +++ b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/factory/PagerFactoryImpl.kt @@ -1,6 +1,6 @@ package com.stslex.atten.core.paging.factory -import com.stslex.atten.core.coroutine.scope.AppCoroutineScope +import com.stslex.atten.core.core.coroutine.scope.AppCoroutineScope import com.stslex.atten.core.paging.holder.ItemHolder import com.stslex.atten.core.paging.model.PagingConfig import com.stslex.atten.core.paging.model.PagingItem diff --git a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/holder/ItemHolderImpl.kt b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/holder/ItemHolderImpl.kt index e99d8f5..542deac 100644 --- a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/holder/ItemHolderImpl.kt +++ b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/holder/ItemHolderImpl.kt @@ -8,7 +8,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow -class ItemHolderImpl : ItemHolder { +open class ItemHolderImpl : ItemHolder { private val _items = MutableStateFlow>(emptyList()) override val items: StateFlow> = _items.asStateFlow() diff --git a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/model/PagingResponse.kt b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/model/PagingResponse.kt index 33bc8a3..1438d0c 100644 --- a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/model/PagingResponse.kt +++ b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/model/PagingResponse.kt @@ -1,6 +1,6 @@ package com.stslex.atten.core.paging.model -import com.stslex.atten.core.coroutine.asyncMap +import com.stslex.atten.core.core.coroutine.asyncMap import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/pager/PagerImpl.kt b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/pager/PagerImpl.kt index e620b88..14132fd 100644 --- a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/pager/PagerImpl.kt +++ b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/pager/PagerImpl.kt @@ -1,7 +1,7 @@ package com.stslex.atten.core.paging.pager -import com.stslex.atten.core.coroutine.scope.AppCoroutineScope -import com.stslex.atten.core.logger.Log +import com.stslex.atten.core.core.coroutine.scope.AppCoroutineScope +import com.stslex.atten.core.core.logger.Log import com.stslex.atten.core.paging.holder.ItemHolder import com.stslex.atten.core.paging.holder.ItemLoaderEvent import com.stslex.atten.core.paging.model.PagingConfig diff --git a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/states/PagingState.kt b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/states/PagingState.kt index de1a1c7..4ffddb8 100644 --- a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/states/PagingState.kt +++ b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/states/PagingState.kt @@ -1,6 +1,6 @@ package com.stslex.atten.core.paging.states -import com.stslex.atten.core.coroutine.asyncMap +import com.stslex.atten.core.core.coroutine.asyncMap import com.stslex.atten.core.paging.model.PagingConfig import com.stslex.atten.core.paging.model.PagingItem import com.stslex.atten.core.paging.model.PagingResponse diff --git a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/worker/PagingWorkerImpl.kt b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/worker/PagingWorkerImpl.kt index 2ae6623..5eb60b9 100644 --- a/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/worker/PagingWorkerImpl.kt +++ b/core/paging/src/commonMain/kotlin/com.stslex.atten.core.paging/worker/PagingWorkerImpl.kt @@ -1,6 +1,6 @@ package com.stslex.atten.core.paging.worker -import com.stslex.atten.core.coroutine.scope.AppCoroutineScope +import com.stslex.atten.core.core.coroutine.scope.AppCoroutineScope import com.stslex.atten.core.paging.model.PagingItem import com.stslex.atten.core.paging.model.PagingResponse import kotlinx.coroutines.CoroutineScope diff --git a/core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/data/ToDoItemHolder.kt b/core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/data/ToDoItemHolder.kt new file mode 100644 index 0000000..b9b3c04 --- /dev/null +++ b/core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/data/ToDoItemHolder.kt @@ -0,0 +1,8 @@ +package com.stslex.atten.core.todo.data + +import com.stslex.atten.core.paging.holder.ItemHolderImpl +import com.stslex.atten.core.todo.data.model.ToDoDataModel +import org.koin.core.annotation.Single + +@Single +class ToDoItemHolder : ItemHolderImpl() \ No newline at end of file diff --git a/core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/data/repository/ToDoRepositoryImpl.kt b/core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/data/repository/ToDoRepositoryImpl.kt index aef5de3..0c88916 100644 --- a/core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/data/repository/ToDoRepositoryImpl.kt +++ b/core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/data/repository/ToDoRepositoryImpl.kt @@ -1,17 +1,17 @@ package com.stslex.atten.core.todo.data.repository -import com.stslex.atten.core.coroutine.asyncMap -import com.stslex.atten.core.coroutine.coroutineExceptionHandler -import com.stslex.atten.core.coroutine.dispatcher.AppDispatcher -import com.stslex.atten.core.coroutine.scope.AppCoroutineScope +import com.stslex.atten.core.core.coroutine.asyncMap +import com.stslex.atten.core.core.coroutine.coroutineExceptionHandler +import com.stslex.atten.core.core.coroutine.dispatcher.AppDispatcher +import com.stslex.atten.core.core.coroutine.scope.AppCoroutineScope import com.stslex.atten.core.database.db.ToDoDao import com.stslex.atten.core.paging.factory.PagerFactory -import com.stslex.atten.core.paging.holder.ItemHolder import com.stslex.atten.core.paging.model.PagingResponse import com.stslex.atten.core.paging.states.PagerAction import com.stslex.atten.core.paging.states.PagerLoadEvents import com.stslex.atten.core.paging.states.PagerLoadState import com.stslex.atten.core.paging.states.PagingState +import com.stslex.atten.core.todo.data.ToDoItemHolder import com.stslex.atten.core.todo.data.model.CreateTodoDataModel import com.stslex.atten.core.todo.data.model.ToDoDataModel import com.stslex.atten.core.todo.data.model.UpdateTodoDataModel @@ -26,10 +26,12 @@ import kotlinx.coroutines.async import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.withContext +import org.koin.core.annotation.Single +@Single class ToDoRepositoryImpl( private val dao: ToDoDao, - private val itemsHolder: ItemHolder, + private val itemsHolder: ToDoItemHolder, pagerFactory: PagerFactory, private val appDispatcher: AppDispatcher ) : ToDoRepository { diff --git a/core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/di/ModuleCoreToDo.kt b/core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/di/ModuleCoreToDo.kt index fba6b26..93ec93f 100644 --- a/core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/di/ModuleCoreToDo.kt +++ b/core/todo/src/commonMain/kotlin/com.stslex.atten.core.todo/di/ModuleCoreToDo.kt @@ -1,22 +1,8 @@ package com.stslex.atten.core.todo.di -import com.stslex.atten.core.paging.holder.ItemHolder -import com.stslex.atten.core.paging.holder.ItemHolderImpl -import com.stslex.atten.core.todo.data.model.ToDoDataModel -import com.stslex.atten.core.todo.data.repository.ToDoRepository -import com.stslex.atten.core.todo.data.repository.ToDoRepositoryImpl import org.koin.core.annotation.ComponentScan import org.koin.core.annotation.Module -import org.koin.core.module.dsl.bind -import org.koin.core.module.dsl.singleOf -import org.koin.dsl.module @Module @ComponentScan("com.stslex.atten.core.todo") -class ModuleCoreToDo { - - val module = module { - single> { ItemHolderImpl() } - singleOf(::ToDoRepositoryImpl) { bind() } - } -} +class ModuleCoreToDo diff --git a/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/Store.kt b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/Store.kt index c74b7ff..3da4639 100644 --- a/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/Store.kt +++ b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/Store.kt @@ -2,9 +2,9 @@ package com.stslex.atten.core.ui.kit.mvi import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.stslex.atten.core.coroutine.dispatcher.AppDispatcher -import com.stslex.atten.core.coroutine.scope.AppCoroutineScope -import com.stslex.atten.core.logger.Log +import com.stslex.atten.core.core.coroutine.dispatcher.AppDispatcher +import com.stslex.atten.core.core.coroutine.scope.AppCoroutineScope +import com.stslex.atten.core.core.logger.Log import com.stslex.atten.core.ui.kit.mvi.StoreComponent.Action import com.stslex.atten.core.ui.kit.mvi.StoreComponent.Event import com.stslex.atten.core.ui.kit.mvi.StoreComponent.Navigation diff --git a/core/ui/kit/src/iosMain/kotlin/com.stslex.atten.core.ui/kit/mvi/StoreExt.ios.kt b/core/ui/kit/src/iosMain/kotlin/com.stslex.atten.core.ui/kit/mvi/StoreExt.ios.kt index bd79146..23f11f5 100644 --- a/core/ui/kit/src/iosMain/kotlin/com.stslex.atten.core.ui/kit/mvi/StoreExt.ios.kt +++ b/core/ui/kit/src/iosMain/kotlin/com.stslex.atten.core.ui/kit/mvi/StoreExt.ios.kt @@ -18,4 +18,4 @@ actual inline fun Module.viewModelDefinition( actual inline fun getViewModel( qualifier: Qualifier?, noinline parameters: ParametersDefinition? -): T = koinInject(qualifier, parameters = parameters) \ No newline at end of file +): T = koinInject(qualifier) \ No newline at end of file diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/BaseStore.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/BaseStore.kt index 9c5beef..4d4fa3e 100644 --- a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/BaseStore.kt +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/BaseStore.kt @@ -2,9 +2,9 @@ package com.stslex.atten.core.ui.mvi import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.stslex.atten.core.coroutine.dispatcher.AppDispatcher -import com.stslex.atten.core.coroutine.scope.AppCoroutineScope -import com.stslex.atten.core.logger.Log +import com.stslex.atten.core.core.coroutine.dispatcher.AppDispatcher +import com.stslex.atten.core.core.coroutine.scope.AppCoroutineScope +import com.stslex.atten.core.core.logger.Log import com.stslex.atten.core.ui.mvi.Store.Action import com.stslex.atten.core.ui.mvi.Store.Event import com.stslex.atten.core.ui.mvi.Store.State diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/HandlerStore.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/HandlerStore.kt index ba91879..aa63fc8 100644 --- a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/HandlerStore.kt +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/HandlerStore.kt @@ -1,9 +1,8 @@ package com.stslex.atten.core.ui.mvi.handler -import com.stslex.atten.core.logger.Logger -import com.stslex.atten.core.coroutine.dispatcher.AppDispatcher -import com.stslex.atten.core.coroutine.scope.AppCoroutineScope -import com.stslex.wizard.core.core.coroutine.AppDispatcherImpl +import com.stslex.atten.core.core.coroutine.dispatcher.AppDispatcher +import com.stslex.atten.core.core.coroutine.scope.AppCoroutineScope +import com.stslex.atten.core.core.logger.Logger import com.stslex.atten.core.ui.mvi.Store import com.stslex.atten.core.ui.mvi.Store.Event import com.stslex.atten.core.ui.mvi.Store.State @@ -66,7 +65,7 @@ interface HandlerStore { * */ fun Flow.launch( onError: suspend (cause: Throwable) -> Unit = {}, - workDispatcher: CoroutineDispatcher = AppDispatcherImpl.default, + workDispatcher: CoroutineDispatcher = appDispatcher.default, eachDispatcher: CoroutineDispatcher = appDispatcher.default, each: suspend (T) -> Unit ): Job diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt index 2ce12e1..18bdb30 100644 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt @@ -4,14 +4,12 @@ import com.stslex.atten.core.ui.kit.mvi.storeOf import com.stslex.atten.feature.details.domain.interactor.DetailsInteractor import com.stslex.atten.feature.details.domain.interactor.DetailsInteractorImpl import com.stslex.atten.feature.details.ui.store.DetailsStore -import org.koin.core.annotation.ComponentScan -import org.koin.core.annotation.Module import org.koin.core.module.dsl.bind import org.koin.core.module.dsl.factoryOf import org.koin.dsl.module -@Module -@ComponentScan("com.stslex.atten.feature.details") +//@Module +//@ComponentScan("com.stslex.atten.feature.details") class FeatureDetailsModule { val module = module { diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsGraph.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsGraph.kt index 7697114..02129a0 100644 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsGraph.kt +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsGraph.kt @@ -1,17 +1,5 @@ package com.stslex.atten.feature.details.navigation -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.navigation.NavGraphBuilder -import com.stslex.atten.core.ui.navigation.Component -import com.stslex.atten.core.ui.navigation.navScreen -import com.stslex.atten.core.ui.kit.mvi.getStore -import com.stslex.atten.feature.details.ui.DetailsScreen -import com.stslex.atten.feature.details.ui.store.DetailsStore -import com.stslex.atten.feature.details.ui.store.DetailsStoreComponent.Action - //fun NavGraphBuilder.detailsGraph() { // navScreen { args -> // val store = getStore() diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsRouter.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsRouter.kt index 33752c9..cc0a37a 100644 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsRouter.kt +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsRouter.kt @@ -1,5 +1,6 @@ package com.stslex.atten.feature.details.navigation +import com.arkivanov.decompose.ComponentContext import com.stslex.atten.core.ui.kit.mvi.Router import com.stslex.atten.feature.details.ui.mvi.DetailsComponent import com.stslex.atten.feature.details.ui.store.DetailsStoreComponent.Navigation @@ -8,11 +9,12 @@ interface DetailsRouter : Router class DetailsComponentImpl( private val popBack: () -> Unit, -) : DetailsComponent { + componentContext: ComponentContext, +) : DetailsComponent, ComponentContext by componentContext { - override fun invoke(event: Navigation) { - when (event) { - Navigation.Back -> navigator.popBack() - } - } +// override fun invoke(event: Navigation) { +// when (event) { +// Navigation.Back -> navigator.popBack() +// } +// } } \ No newline at end of file diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStore.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStore.kt index adea887..3ff5926 100644 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStore.kt +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStore.kt @@ -1,7 +1,7 @@ package com.stslex.atten.feature.details.ui.store -import com.stslex.atten.core.logger.Logger -import com.stslex.atten.core.coroutine.dispatcher.AppDispatcher +import com.stslex.atten.core.core.coroutine.dispatcher.AppDispatcher +import com.stslex.atten.core.core.logger.Log import com.stslex.atten.core.ui.kit.mvi.Store import com.stslex.atten.core.ui.kit.mvi.StoreComponent.Event.Snackbar import com.stslex.atten.feature.details.domain.interactor.DetailsInteractor @@ -91,7 +91,7 @@ class DetailsStore( } }, onSuccess = { - Logger.d("Item updated") + Log.d("Item updated") }, onError = { error -> showError(error) diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt index 15285aa..dd47d9c 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt @@ -4,15 +4,13 @@ import com.stslex.atten.core.ui.kit.mvi.storeOf import com.stslex.atten.feature.home.domain.interactor.HomeScreenInteractor import com.stslex.atten.feature.home.domain.interactor.HomeScreenInteractorImpl import com.stslex.atten.feature.home.ui.store.HomeStore -import org.koin.core.annotation.ComponentScan -import org.koin.core.annotation.Module import org.koin.core.module.dsl.bind import org.koin.core.module.dsl.factoryOf import org.koin.dsl.module -@Module -@ComponentScan("com.stslex.atten.feature.home") +//@Module +//@ComponentScan("com.stslex.atten.feature.home") class ModuleFeatureHome { val module = module { diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeGraph.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeGraph.kt index 221fb68..cf8a4f6 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeGraph.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeGraph.kt @@ -1,38 +1,22 @@ package com.stslex.atten.feature.home.navigation -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.hapticfeedback.HapticFeedbackType -import androidx.compose.ui.platform.LocalHapticFeedback -import androidx.navigation.NavGraphBuilder -import com.stslex.atten.core.ui.navigation.Component -import com.stslex.atten.core.ui.navigation.navScreen -import com.stslex.atten.core.ui.kit.mvi.getStore -import com.stslex.atten.feature.home.ui.HomeScreen -import com.stslex.atten.feature.home.ui.store.HomeStore -import com.stslex.atten.feature.home.ui.store.HomeStoreComponent.Action -import com.stslex.atten.feature.home.ui.store.HomeStoreComponent.Event -import kotlinx.coroutines.flow.collectLatest - -fun NavGraphBuilder.homeGraph() { - navScreen { - val store = getStore() - val state by remember { store.state }.collectAsState() - val hapticFeedback = LocalHapticFeedback.current - - LaunchedEffect(Unit) { - store.dispatch(Action.Init) - - store.event.collectLatest { - when (it) { - is Event.Snackbar -> { - // show snackbar - } - } - } - } +//fun NavGraphBuilder.homeGraph() { +// navScreen { +// val store = getStore() +// val state by remember { store.state }.collectAsState() +// val hapticFeedback = LocalHapticFeedback.current +// +// LaunchedEffect(Unit) { +// store.dispatch(Action.Init) +// +// store.event.collectLatest { +// when (it) { +// is Event.Snackbar -> { +// // show snackbar +// } +// } +// } +// } // todo: add back handler // AppBackHandler( // enabled = state.selectedItems.isNotEmpty(), @@ -41,27 +25,27 @@ fun NavGraphBuilder.homeGraph() { // } // ) - HomeScreen( - state = state, - onItemClicked = { id -> - hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove) - store.dispatch(Action.OnItemClicked(id)) - }, - onLoadNext = { - store.dispatch(Action.LoadMore) - }, - onCreateItemClick = { - hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove) - store.dispatch(Action.OnCreateItemClicked) - }, - onDeleteItemsClick = { - hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove) - store.dispatch(Action.OnDeleteItemsClicked) - }, - onItemLongCLick = { id -> - hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) - store.dispatch(Action.OnSelectItemClicked(id)) - } - ) - } -} +// HomeScreen( +// state = state, +// onItemClicked = { id -> +// hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove) +// store.dispatch(Action.OnItemClicked(id)) +// }, +// onLoadNext = { +// store.dispatch(Action.LoadMore) +// }, +// onCreateItemClick = { +// hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove) +// store.dispatch(Action.OnCreateItemClicked) +// }, +// onDeleteItemsClick = { +// hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove) +// store.dispatch(Action.OnDeleteItemsClicked) +// }, +// onItemLongCLick = { id -> +// hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) +// store.dispatch(Action.OnSelectItemClicked(id)) +// } +// ) +// } +//} diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouterImpl.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouterImpl.kt deleted file mode 100644 index d9c0371..0000000 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouterImpl.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.stslex.atten.feature.home.navigation - -import com.stslex.atten.core.ui.navigation.Component -import com.stslex.atten.core.ui.navigation.Navigator -import com.stslex.atten.feature.home.ui.store.HomeStoreComponent.Navigation - -class HomeRouterImpl( - private val navigator: Navigator -) : HomeRouter { - - override fun invoke(event: Navigation) { - when (event) { - is Navigation.NavigateToDetail -> navigator.navigateTo(Component.DetailScreen(event.id)) - is Navigation.Back -> navigator.popBack() - } - } -} \ No newline at end of file diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeRouterImpl.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeRouterImpl.kt new file mode 100644 index 0000000..ef810ae --- /dev/null +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeRouterImpl.kt @@ -0,0 +1,24 @@ +package com.stslex.atten.feature.home.ui.mvi + +import com.arkivanov.decompose.ComponentContext +import com.stslex.atten.core.ui.navigation.Component +import com.stslex.atten.feature.home.ui.store.HomeStoreComponent + +class HomeComponentImpl( + private val popBack: () -> Unit, + private val navTo: () -> Unit, + componentContext: ComponentContext +) : HomeComponent, ComponentContext by componentContext { + +// override fun invoke(event: HomeStoreComponent.Navigation) { +// when (event) { +// is HomeStoreComponent.Navigation.NavigateToDetail -> navigator.navigateTo( +// Component.DetailScreen( +// event.id +// ) +// ) +// +// is HomeStoreComponent.Navigation.Back -> navigator.popBack() +// } +// } +} \ No newline at end of file diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStore.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStore.kt index d26f05e..932ccbe 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStore.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStore.kt @@ -1,6 +1,6 @@ package com.stslex.atten.feature.home.ui.store -import com.stslex.atten.core.coroutine.dispatcher.AppDispatcher +import com.stslex.atten.core.core.coroutine.dispatcher.AppDispatcher import com.stslex.atten.core.paging.states.PagerAction import com.stslex.atten.core.paging.states.PagerLoadState import com.stslex.atten.core.paging.states.pagingMap From f4d8863a00763ca5c9fadf172fb4609997f94d38 Mon Sep 17 00:00:00 2001 From: stslex Date: Fri, 27 Jun 2025 10:14:56 +0300 Subject: [PATCH 4/8] fix ksp task for room/koin --- .../kotlin/RoomLibraryConventionPlugin.kt | 13 +++---- .../atten/convention/KotlinMultiplatform.kt | 22 ++++++++--- .../atten/convention/configs/KspConfig.kt | 37 +++++++++++++++++++ .../atten/core/database/db/AppDatabase.kt | 7 ---- .../database/db/AppDatabaseConstructor.kt | 9 +++++ .../di/FeatureDetailsModule.kt | 37 ++++++++++++++----- .../di/ModuleFeatureHome.kt | 37 ++++++++++++++----- 7 files changed, 123 insertions(+), 39 deletions(-) create mode 100644 build-logic/convention/src/main/kotlin/com/stslex/atten/convention/configs/KspConfig.kt create mode 100644 core/database/src/commonMain/kotlin/com/stslex/atten/core/database/db/AppDatabaseConstructor.kt diff --git a/build-logic/convention/src/main/kotlin/RoomLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/RoomLibraryConventionPlugin.kt index 9444ba0..ee317ba 100644 --- a/build-logic/convention/src/main/kotlin/RoomLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/RoomLibraryConventionPlugin.kt @@ -1,6 +1,7 @@ import AppExt.findPluginId import AppExt.libs import androidx.room.gradle.RoomExtension +import com.stslex.atten.convention.configs.KspConfig import com.stslex.atten.convention.configureKsp import org.gradle.api.Plugin import org.gradle.api.Project @@ -29,14 +30,12 @@ class RoomLibraryConventionPlugin : Plugin { schemaDirectory("$projectDir/schemas") } + dependencies { + val roomCompiler = libs.findLibrary("room-compiler").get() + KspConfig.platform.forEach { task -> add(task.configName, roomCompiler) } + } + extensions.configure { - dependencies { - val roomCompiler = libs.findLibrary("room-compiler").get() - add("kspAndroid", roomCompiler) - add("kspIosSimulatorArm64", roomCompiler) - add("kspIosX64", roomCompiler) - add("kspIosArm64", roomCompiler) - } sourceSets.apply { commonMain.dependencies { implementation(libs.findLibrary("room-runtime").get()) diff --git a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinMultiplatform.kt b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinMultiplatform.kt index 4963b3f..d8a4cc3 100644 --- a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinMultiplatform.kt +++ b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/KotlinMultiplatform.kt @@ -1,10 +1,12 @@ package com.stslex.atten.convention import AppExt.libs +import com.stslex.atten.convention.configs.KspConfig import org.gradle.api.Project import org.gradle.api.plugins.ExtensionAware import org.gradle.kotlin.dsl.configure import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.withType import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.plugin.cocoapods.CocoapodsExtension import org.jetbrains.kotlin.gradle.tasks.KotlinCompile @@ -22,7 +24,7 @@ internal fun Project.configureKotlinMultiplatform( applyDefaultHierarchyTemplate() dependencies { - add("kspCommonMainMetadata", libs.findLibrary("koin-ksp-compiler").get()) + add(KspConfig.COMMON_MAIN.configName, libs.findLibrary("koin-ksp-compiler").get()) } //common dependencies @@ -58,7 +60,17 @@ internal fun Project.configureKotlinMultiplatform( //applying the Cocoapods Configuration we made (this as ExtensionAware).extensions.configure(::configureKotlinCocoapods) -// tasks.withType(KotlinCompile::class.java).all { -// if (name != "kspCommonMainKotlinMetadata") dependsOn("kspCommonMainKotlinMetadata") -// } -} \ No newline at end of file + + tasks.withType().configureEach { + if (name != KspConfig.COMMON_MAIN.taskName) { + dependsOn(KspConfig.COMMON_MAIN.taskName) + } + } + + tasks.matching { task -> + task.name.startsWith("ksp") && task.name != KspConfig.COMMON_MAIN.taskName + } + .configureEach { + dependsOn(KspConfig.COMMON_MAIN.taskName) + } +} diff --git a/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/configs/KspConfig.kt b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/configs/KspConfig.kt new file mode 100644 index 0000000..174c722 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/com/stslex/atten/convention/configs/KspConfig.kt @@ -0,0 +1,37 @@ +package com.stslex.atten.convention.configs + +enum class KspConfig( + val configName: String, + val taskName: String, +) { + COMMON_MAIN( + configName = "kspCommonMainMetadata", + taskName = "kspCommonMainKotlinMetadata" + ), + ANDROID( + configName = "kspAndroid", + taskName = "kspKotlinAndroid" + ), + IOS_SIMULATOR_ARM64( + configName = "kspIosSimulatorArm64", + taskName = "kspKotlinIosSimulatorArm64" + ), + IOS_X64( + configName = "kspIosX64", + taskName = "kspKotlinIosX64" + ), + IOS_ARM64( + configName = "kspIosArm64", + taskName = "kspKotlinIosArm64" + ); + + companion object { + + val platform = listOf( + ANDROID, + IOS_SIMULATOR_ARM64, + IOS_X64, + IOS_ARM64 + ) + } +} \ No newline at end of file diff --git a/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/db/AppDatabase.kt b/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/db/AppDatabase.kt index 092dcfe..3c74016 100644 --- a/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/db/AppDatabase.kt +++ b/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/db/AppDatabase.kt @@ -3,7 +3,6 @@ package com.stslex.atten.core.database.db import androidx.room.ConstructedBy import androidx.room.Database import androidx.room.RoomDatabase -import androidx.room.RoomDatabaseConstructor import androidx.sqlite.SQLiteConnection import androidx.sqlite.driver.bundled.BundledSQLiteDriver import androidx.sqlite.execSQL @@ -26,12 +25,6 @@ abstract class AppDatabase : RoomDatabase() { } } -// The Room compiler generates the `actual` implementations. -@Suppress("NO_ACTUAL_FOR_EXPECT") -expect object AppDatabaseConstructor : RoomDatabaseConstructor { - override fun initialize(): AppDatabase -} - fun getRoomDatabase( builder: RoomDatabase.Builder ): AppDatabase { diff --git a/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/db/AppDatabaseConstructor.kt b/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/db/AppDatabaseConstructor.kt new file mode 100644 index 0000000..b74ecdb --- /dev/null +++ b/core/database/src/commonMain/kotlin/com/stslex/atten/core/database/db/AppDatabaseConstructor.kt @@ -0,0 +1,9 @@ +package com.stslex.atten.core.database.db + +import androidx.room.RoomDatabaseConstructor + +// The Room compiler generates the `actual` implementations. +@Suppress("NO_ACTUAL_FOR_EXPECT") +expect object AppDatabaseConstructor : RoomDatabaseConstructor { + override fun initialize(): AppDatabase +} \ No newline at end of file diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt index 18bdb30..f01a2a6 100644 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt @@ -1,20 +1,37 @@ package com.stslex.atten.feature.details.di -import com.stslex.atten.core.ui.kit.mvi.storeOf +import com.stslex.atten.core.core.coroutine.dispatcher.AppDispatcher +import com.stslex.atten.core.todo.data.repository.ToDoRepository import com.stslex.atten.feature.details.domain.interactor.DetailsInteractor import com.stslex.atten.feature.details.domain.interactor.DetailsInteractorImpl +import com.stslex.atten.feature.details.navigation.DetailsRouter import com.stslex.atten.feature.details.ui.store.DetailsStore -import org.koin.core.module.dsl.bind -import org.koin.core.module.dsl.factoryOf -import org.koin.dsl.module +import com.stslex.atten.feature.details.ui.store.DetailsStoreComponent +import org.koin.android.annotation.KoinViewModel +import org.koin.core.annotation.ComponentScan +import org.koin.core.annotation.Factory +import org.koin.core.annotation.Module -//@Module -//@ComponentScan("com.stslex.atten.feature.details") +@Module +@ComponentScan("com.stslex.atten.feature.details") class FeatureDetailsModule { - val module = module { - factoryOf(::DetailsInteractorImpl) { bind() } -// factoryOf(::DetailsRouterImpl) { bind() } - storeOf(::DetailsStore) + @Factory + fun interactor( + repository: ToDoRepository + ): DetailsInteractor = DetailsInteractorImpl(repository) + + @KoinViewModel + fun store( + interactor: DetailsInteractor, + appDispatcher: AppDispatcher, + router: DetailsRouter, + ): DetailsStore = DetailsStore(interactor, appDispatcher, router) + + @Factory + fun router(): DetailsRouter = object : DetailsRouter { + override fun invoke(event: DetailsStoreComponent.Navigation) { + TODO("Not yet implemented") + } } } diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt index dd47d9c..bde79f6 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt @@ -1,21 +1,38 @@ package com.stslex.atten.feature.home.di -import com.stslex.atten.core.ui.kit.mvi.storeOf +import com.stslex.atten.core.core.coroutine.dispatcher.AppDispatcher +import com.stslex.atten.core.todo.data.repository.ToDoRepository import com.stslex.atten.feature.home.domain.interactor.HomeScreenInteractor import com.stslex.atten.feature.home.domain.interactor.HomeScreenInteractorImpl +import com.stslex.atten.feature.home.navigation.HomeRouter import com.stslex.atten.feature.home.ui.store.HomeStore -import org.koin.core.module.dsl.bind -import org.koin.core.module.dsl.factoryOf -import org.koin.dsl.module +import com.stslex.atten.feature.home.ui.store.HomeStoreComponent +import org.koin.android.annotation.KoinViewModel +import org.koin.core.annotation.ComponentScan +import org.koin.core.annotation.Factory +import org.koin.core.annotation.Module -//@Module -//@ComponentScan("com.stslex.atten.feature.home") +@Module +@ComponentScan("com.stslex.atten.feature.home") class ModuleFeatureHome { - val module = module { - factoryOf(::HomeScreenInteractorImpl) { bind() } -// factoryOf(::HomeRouterImpl) { bind() } - storeOf(::HomeStore) + @Factory + fun interactor( + repository: ToDoRepository + ): HomeScreenInteractor = HomeScreenInteractorImpl(repository) + + @KoinViewModel + fun store( + interactor: HomeScreenInteractor, + appDispatcher: AppDispatcher, + router: HomeRouter, + ): HomeStore = HomeStore(interactor, appDispatcher, router) + + @Factory + fun router(): HomeRouter = object : HomeRouter { + override fun invoke(event: HomeStoreComponent.Navigation) { + TODO("Not yet implemented") + } } } From e28a5c5e0da8afd2758125b20cf4c3ee3cd177dd Mon Sep 17 00:00:00 2001 From: stslex Date: Mon, 30 Jun 2025 19:29:41 +0300 Subject: [PATCH 5/8] refactor mvi for details/home --- .../kotlin/KMPApplicationConventionPlugin.kt | 2 + commonApp/build.gradle.kts | 2 +- .../kotlin/com/stslex/atten/MainActivity.kt | 4 +- .../commonMain/kotlin/com/stslex/atten/App.kt | 4 +- .../kotlin/com/stslex/atten/di/AppModules.kt | 4 +- .../stslex/atten/host/AppNavigationHost.kt | 6 +- .../stslex/atten/host/DefaultRootComponent.kt | 19 +- .../atten/core/ui/kit/mvi/StoreExt.android.kt | 25 -- .../core/ui/kit/theme/AppTheme.android.kt | 25 +- .../stslex/atten/core/ui/kit/mvi/StoreExt.kt | 60 ----- .../kit/mvi/StoreExt.ios.kt | 21 -- .../com/stslex/atten/core/ui/mvi/BaseStore.kt | 15 +- .../com/stslex/atten/core/ui/mvi/Feature.kt | 6 + .../atten/core/ui/mvi/NavComponentScreen.kt | 7 +- .../com/stslex/atten/core/ui/mvi/StoreExt.kt | 2 +- .../atten/core/ui/mvi/handler/Handler.kt | 3 +- .../core/ui/mvi/handler/HandlerCreator.kt | 3 +- .../core/ui/mvi/processor/StoreProcessor.kt | 10 +- .../stslex/atten/core/ui/navigation/Config.kt | 7 +- .../feature/details/DetailsScreenPreview.kt | 6 +- .../di/DetailsFeature.kt | 43 ++++ .../di/DetailsScope.kt | 6 + .../di/FeatureDetailsModule.kt | 37 --- .../di/ModuleFeatureDetails.kt | 8 + .../interactor/DetailsInteractorImpl.kt | 7 + .../navigation/DetailsGraph.kt | 23 -- .../navigation/DetailsRouter.kt | 20 -- .../ui/DetailsScreen.kt | 81 +------ .../ui/DetailsWidget.kt | 78 +++++++ .../ui/mvi/DetailsComponent.kt | 22 +- .../ui/mvi/DetailsHandlerStore.kt | 8 + .../ui/mvi/DetailsStore.kt | 85 +++++++ .../ui/mvi/DetailsStoreImpl.kt | 42 ++++ .../ui/{store => mvi}/ScreenState.kt | 2 +- .../ui/mvi/handlers/ClickHandler.kt | 59 +++++ .../ui/mvi/handlers/CommonHandler.kt | 86 +++++++ .../ui/mvi/handlers/DetailsComponentImpl.kt | 19 ++ .../ui/mvi/handlers/InputHandler.kt | 47 ++++ .../ui/store/DetailsStore.kt | 164 ------------- .../ui/store/DetailsStoreComponent.kt | 65 ------ .../di/HomeFeature.kt | 43 ++++ .../di/HomeScope.kt | 6 + .../di/ModuleFeatureHome.kt | 31 +-- .../interactor/HomeScreenInteractorImpl.kt | 7 + .../navigation/HomeGraph.kt | 51 ---- .../navigation/HomeRouter.kt | 6 - .../ui/HomeScreen.kt | 199 ++++------------ .../ui/HomeWidget.kt | 165 +++++++++++++ .../ui/mvi/HomeComponent.kt | 21 +- .../ui/mvi/HomeHandlerStore.kt | 8 + .../ui/mvi/HomeRouterImpl.kt | 24 -- .../ui/mvi/HomeStore.kt | 111 +++++++++ .../ui/mvi/HomeStoreImpl.kt | 43 ++++ .../ui/{store => mvi}/ScreenState.kt | 2 +- .../ui/mvi/handlers/ClickHandler.kt | 106 +++++++++ .../ui/mvi/handlers/CommonHandler.kt | 39 ++++ .../ui/mvi/handlers/HomeComponentImpl.kt | 21 ++ .../ui/mvi/handlers/PagingHandler.kt | 117 ++++++++++ .../ui/store/HomeStore.kt | 220 ------------------ .../ui/store/HomeStoreComponent.kt | 85 ------- 60 files changed, 1335 insertions(+), 1103 deletions(-) delete mode 100644 core/ui/kit/src/androidMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreExt.android.kt delete mode 100644 core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreExt.kt delete mode 100644 core/ui/kit/src/iosMain/kotlin/com.stslex.atten.core.ui/kit/mvi/StoreExt.ios.kt create mode 100644 feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/DetailsFeature.kt create mode 100644 feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/DetailsScope.kt delete mode 100644 feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt create mode 100644 feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/ModuleFeatureDetails.kt delete mode 100644 feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsGraph.kt delete mode 100644 feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsRouter.kt create mode 100644 feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/DetailsWidget.kt create mode 100644 feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsHandlerStore.kt create mode 100644 feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsStore.kt create mode 100644 feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsStoreImpl.kt rename feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/{store => mvi}/ScreenState.kt (83%) create mode 100644 feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/handlers/ClickHandler.kt create mode 100644 feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/handlers/CommonHandler.kt create mode 100644 feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/handlers/DetailsComponentImpl.kt create mode 100644 feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/handlers/InputHandler.kt delete mode 100644 feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStore.kt delete mode 100644 feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStoreComponent.kt create mode 100644 feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/HomeFeature.kt create mode 100644 feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/HomeScope.kt delete mode 100644 feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeGraph.kt delete mode 100644 feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouter.kt create mode 100644 feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/HomeWidget.kt create mode 100644 feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeHandlerStore.kt delete mode 100644 feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeRouterImpl.kt create mode 100644 feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeStore.kt create mode 100644 feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeStoreImpl.kt rename feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/{store => mvi}/ScreenState.kt (95%) create mode 100644 feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/handlers/ClickHandler.kt create mode 100644 feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/handlers/CommonHandler.kt create mode 100644 feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/handlers/HomeComponentImpl.kt create mode 100644 feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/handlers/PagingHandler.kt delete mode 100644 feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStore.kt delete mode 100644 feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStoreComponent.kt diff --git a/build-logic/convention/src/main/kotlin/KMPApplicationConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KMPApplicationConventionPlugin.kt index 5533ede..1de51fd 100644 --- a/build-logic/convention/src/main/kotlin/KMPApplicationConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/KMPApplicationConventionPlugin.kt @@ -5,6 +5,7 @@ import AppExt.findVersionString import AppExt.libs import com.android.build.api.dsl.ApplicationExtension import com.stslex.atten.convention.configureKMPCompose +import com.stslex.atten.convention.configureKMPComposeNavigation import com.stslex.atten.convention.configureKotlin import com.stslex.atten.convention.configureKotlinAndroid import com.stslex.atten.convention.configureKotlinAndroidCompose @@ -30,6 +31,7 @@ class KMPApplicationConventionPlugin : Plugin { configureKotlinMultiplatform() configureKMPCompose() configureKotlin() + configureKMPComposeNavigation() extensions.configure { configureKotlinAndroid(this) diff --git a/commonApp/build.gradle.kts b/commonApp/build.gradle.kts index aedfaa1..67423a3 100644 --- a/commonApp/build.gradle.kts +++ b/commonApp/build.gradle.kts @@ -1,6 +1,5 @@ plugins { alias(libs.plugins.convention.kmp.application) - alias(libs.plugins.convention.kmp.navigation) } kotlin { @@ -10,6 +9,7 @@ kotlin { implementation(project(":core:core")) implementation(project(":core:ui:kit")) implementation(project(":core:ui:navigation")) + implementation(project(":core:ui:mvi")) implementation(project(":core:database")) implementation(project(":core:paging")) implementation(project(":core:todo")) diff --git a/commonApp/src/androidMain/kotlin/com/stslex/atten/MainActivity.kt b/commonApp/src/androidMain/kotlin/com/stslex/atten/MainActivity.kt index 9d06946..a839a4a 100644 --- a/commonApp/src/androidMain/kotlin/com/stslex/atten/MainActivity.kt +++ b/commonApp/src/androidMain/kotlin/com/stslex/atten/MainActivity.kt @@ -35,9 +35,7 @@ class MainActivity : ComponentActivity() { fun AppAndroidPreview() { App( rootComponent = DefaultRootComponent( - componentContext = DefaultComponentContext( - LocalLifecycleOwner.current.lifecycle - ), + componentContext = DefaultComponentContext(LocalLifecycleOwner.current.lifecycle), ), ) } diff --git a/commonApp/src/commonMain/kotlin/com/stslex/atten/App.kt b/commonApp/src/commonMain/kotlin/com/stslex/atten/App.kt index d8d92fb..2729a78 100644 --- a/commonApp/src/commonMain/kotlin/com/stslex/atten/App.kt +++ b/commonApp/src/commonMain/kotlin/com/stslex/atten/App.kt @@ -16,7 +16,9 @@ fun App( rootComponent: RootComponent, onThemeChange: (isDark: Boolean) -> Unit = {}, ) { - AppTheme { + AppTheme( + onThemeChange = onThemeChange + ) { Scaffold( modifier = Modifier .fillMaxSize() diff --git a/commonApp/src/commonMain/kotlin/com/stslex/atten/di/AppModules.kt b/commonApp/src/commonMain/kotlin/com/stslex/atten/di/AppModules.kt index 33b43ff..90259f3 100644 --- a/commonApp/src/commonMain/kotlin/com/stslex/atten/di/AppModules.kt +++ b/commonApp/src/commonMain/kotlin/com/stslex/atten/di/AppModules.kt @@ -4,7 +4,7 @@ import com.stslex.atten.core.core.di.ModuleCore import com.stslex.atten.core.database.di.ModuleCoreDatabase import com.stslex.atten.core.paging.di.ModuleCorePaging import com.stslex.atten.core.todo.di.ModuleCoreToDo -import com.stslex.atten.feature.details.di.FeatureDetailsModule +import com.stslex.atten.feature.details.di.ModuleFeatureDetails import com.stslex.atten.feature.home.di.ModuleFeatureHome import org.koin.core.module.Module import org.koin.ksp.generated.module @@ -15,5 +15,5 @@ val appModules: List = listOf( ModuleCoreToDo().module, ModuleCorePaging().module, ModuleFeatureHome().module, - FeatureDetailsModule().module, + ModuleFeatureDetails().module, ) diff --git a/commonApp/src/commonMain/kotlin/com/stslex/atten/host/AppNavigationHost.kt b/commonApp/src/commonMain/kotlin/com/stslex/atten/host/AppNavigationHost.kt index 3211204..cb6ff8c 100644 --- a/commonApp/src/commonMain/kotlin/com/stslex/atten/host/AppNavigationHost.kt +++ b/commonApp/src/commonMain/kotlin/com/stslex/atten/host/AppNavigationHost.kt @@ -5,6 +5,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.arkivanov.decompose.extensions.compose.stack.Children import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation +import com.stslex.atten.feature.details.ui.DetailsScreen +import com.stslex.atten.feature.home.ui.HomeScreen @Composable internal fun AppNavigationHost( @@ -17,8 +19,8 @@ internal fun AppNavigationHost( animation = stackAnimation(), ) { created -> when (val instance = created.instance) { - is RootComponent.Child.Details -> TODO() - is RootComponent.Child.Home -> TODO() + is RootComponent.Child.Details -> DetailsScreen(instance.component) + is RootComponent.Child.Home -> HomeScreen(instance.component) } } } \ No newline at end of file diff --git a/commonApp/src/commonMain/kotlin/com/stslex/atten/host/DefaultRootComponent.kt b/commonApp/src/commonMain/kotlin/com/stslex/atten/host/DefaultRootComponent.kt index 6969aab..9323eea 100644 --- a/commonApp/src/commonMain/kotlin/com/stslex/atten/host/DefaultRootComponent.kt +++ b/commonApp/src/commonMain/kotlin/com/stslex/atten/host/DefaultRootComponent.kt @@ -9,6 +9,8 @@ import com.arkivanov.decompose.router.stack.navigate import com.arkivanov.decompose.router.stack.pop import com.arkivanov.decompose.value.Value import com.stslex.atten.core.ui.navigation.Config +import com.stslex.atten.feature.details.ui.mvi.DetailsComponent +import com.stslex.atten.feature.home.ui.mvi.HomeComponent import com.stslex.atten.host.RootComponent.Child class DefaultRootComponent( @@ -35,8 +37,21 @@ class DefaultRootComponent( config: Config, context: ComponentContext ): Child = when (config) { - is Config.Home -> Child.Details(TODO()) - is Config.Detail -> Child.Home(TODO()) + is Config.Home -> Child.Home( + HomeComponent.create( + popBack = ::popBack, + navTo = ::navigateTo, + componentContext = context + ) + ) + + is Config.Detail -> Child.Details( + DetailsComponent.create( + popBack = ::popBack, + componentContext = context, + uuid = config.uuid + ) + ) } @OptIn(DelicateDecomposeApi::class) diff --git a/core/ui/kit/src/androidMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreExt.android.kt b/core/ui/kit/src/androidMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreExt.android.kt deleted file mode 100644 index 75512b5..0000000 --- a/core/ui/kit/src/androidMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreExt.android.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.stslex.atten.core.ui.kit.mvi - -import androidx.compose.runtime.Composable -import androidx.lifecycle.ViewModel -import org.koin.androidx.compose.koinViewModel -import org.koin.androidx.viewmodel.dsl.viewModel -import org.koin.core.definition.Definition -import org.koin.core.definition.KoinDefinition -import org.koin.core.module.Module -import org.koin.core.parameter.ParametersDefinition -import org.koin.core.qualifier.Qualifier - -actual inline fun Module.viewModelDefinition( - qualifier: Qualifier?, - noinline definition: Definition -): KoinDefinition = viewModel(qualifier, definition) - -@Composable -actual inline fun getViewModel( - qualifier: Qualifier?, - noinline parameters: ParametersDefinition? -): T = koinViewModel( - qualifier = qualifier, - parameters = parameters -) \ No newline at end of file diff --git a/core/ui/kit/src/androidMain/kotlin/com/stslex/atten/core/ui/kit/theme/AppTheme.android.kt b/core/ui/kit/src/androidMain/kotlin/com/stslex/atten/core/ui/kit/theme/AppTheme.android.kt index 62551a5..5f4fdcf 100644 --- a/core/ui/kit/src/androidMain/kotlin/com/stslex/atten/core/ui/kit/theme/AppTheme.android.kt +++ b/core/ui/kit/src/androidMain/kotlin/com/stslex/atten/core/ui/kit/theme/AppTheme.android.kt @@ -10,17 +10,16 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalContext @Composable -actual fun appColorScheme( - isDarkTheme: Boolean -): ColorScheme = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - val context = LocalContext.current - if (isDarkTheme) { - dynamicDarkColorScheme(context) - } else { - dynamicLightColorScheme(context) +actual fun appColorScheme(isDarkTheme: Boolean): ColorScheme = when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (isDarkTheme) { + dynamicDarkColorScheme(context) + } else { + dynamicLightColorScheme(context) + } } -} else if (isDarkTheme) { - darkColorScheme() -} else { - lightColorScheme() -} \ No newline at end of file + + isDarkTheme -> darkColorScheme() + else -> lightColorScheme() +} diff --git a/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreExt.kt b/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreExt.kt deleted file mode 100644 index 917a9f7..0000000 --- a/core/ui/kit/src/commonMain/kotlin/com/stslex/atten/core/ui/kit/mvi/StoreExt.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.stslex.atten.core.ui.kit.mvi - -import androidx.compose.runtime.Composable -import androidx.lifecycle.ViewModel -import org.koin.core.definition.Definition -import org.koin.core.definition.KoinDefinition -import org.koin.core.module.Module -import org.koin.core.module.dsl.DefinitionOptions -import org.koin.core.module.dsl.new -import org.koin.core.module.dsl.onOptions -import org.koin.core.parameter.ParametersDefinition -import org.koin.core.qualifier.Qualifier - -expect inline fun Module.viewModelDefinition( - qualifier: Qualifier? = null, - noinline definition: Definition -): KoinDefinition - -@Composable -expect inline fun getViewModel( - qualifier: Qualifier? = null, - noinline parameters: ParametersDefinition? = null -): T - -@Composable -inline fun > getStore( - qualifier: Qualifier? = null, - noinline parameters: ParametersDefinition? = null -): T = getViewModel(qualifier, parameters) - -inline fun > Module.storeDefinition( - qualifier: Qualifier? = null, - noinline definition: Definition -): KoinDefinition = viewModelDefinition(qualifier, definition) - -inline fun > Module.storeOf( - crossinline constructor: () -> R, - noinline options: DefinitionOptions? = null, -): KoinDefinition = storeDefinition { new(constructor) }.onOptions(options) - -inline fun , reified T1> Module.storeOf( - crossinline constructor: (T1) -> R, - noinline options: DefinitionOptions? = null, -): KoinDefinition = storeDefinition { new(constructor) }.onOptions(options) - -inline fun , reified T1, reified T2> Module.storeOf( - crossinline constructor: (T1, T2) -> R, - noinline options: DefinitionOptions? = null, -): KoinDefinition = storeDefinition { new(constructor) }.onOptions(options) - -inline fun , reified T1, reified T2, reified T3> Module.storeOf( - crossinline constructor: (T1, T2, T3) -> R, - noinline options: DefinitionOptions? = null, -): KoinDefinition = storeDefinition { new(constructor) }.onOptions(options) - -inline fun , reified T1, reified T2, reified T3, reified T4> Module.storeOf( - crossinline constructor: (T1, T2, T3, T4) -> R, - noinline options: DefinitionOptions? = null, -): KoinDefinition = storeDefinition { new(constructor) }.onOptions(options) - diff --git a/core/ui/kit/src/iosMain/kotlin/com.stslex.atten.core.ui/kit/mvi/StoreExt.ios.kt b/core/ui/kit/src/iosMain/kotlin/com.stslex.atten.core.ui/kit/mvi/StoreExt.ios.kt deleted file mode 100644 index 23f11f5..0000000 --- a/core/ui/kit/src/iosMain/kotlin/com.stslex.atten.core.ui/kit/mvi/StoreExt.ios.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.stslex.atten.core.ui.kit.mvi - -import androidx.compose.runtime.Composable -import androidx.lifecycle.ViewModel -import org.koin.compose.koinInject -import org.koin.core.definition.Definition -import org.koin.core.definition.KoinDefinition -import org.koin.core.module.Module -import org.koin.core.parameter.ParametersDefinition -import org.koin.core.qualifier.Qualifier - -actual inline fun Module.viewModelDefinition( - qualifier: Qualifier?, - noinline definition: Definition -): KoinDefinition = factory(qualifier, definition) - -@Composable -actual inline fun getViewModel( - qualifier: Qualifier?, - noinline parameters: ParametersDefinition? -): T = koinInject(qualifier) \ No newline at end of file diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/BaseStore.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/BaseStore.kt index 4d4fa3e..5d3a19d 100644 --- a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/BaseStore.kt +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/BaseStore.kt @@ -1,5 +1,6 @@ package com.stslex.atten.core.ui.mvi +import androidx.compose.runtime.Immutable import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.stslex.atten.core.core.coroutine.dispatcher.AppDispatcher @@ -8,9 +9,9 @@ import com.stslex.atten.core.core.logger.Log import com.stslex.atten.core.ui.mvi.Store.Action import com.stslex.atten.core.ui.mvi.Store.Event import com.stslex.atten.core.ui.mvi.Store.State +import com.stslex.atten.core.ui.mvi.handler.Handler +import com.stslex.atten.core.ui.mvi.handler.HandlerCreator import com.stslex.atten.core.ui.mvi.handler.HandlerStore -import com.stslex.wizard.core.ui.mvi.v2.Handler -import com.stslex.wizard.core.ui.mvi.v2.HandlerCreator import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job @@ -33,15 +34,17 @@ import kotlinx.coroutines.flow.update * @param HStore The type of the handler store, which provides context to action handlers. Must inherit from [handler.HandlerStore]. * @param name A descriptive name for the store, used for logging. * @param initialState The initial state of the store. - * @param handlerCreator A factory function that creates an [com.stslex.wizard.core.ui.mvi.v2.Handler] for a given action. + * @param handlerCreator A factory function that creates an [Handler] for a given action. * @param initialActions A list of actions to be consumed immediately after the store is initialized. Defaults to an empty list. */ +@Immutable open class BaseStore>( name: String, initialState: S, override val appDispatcher: AppDispatcher, private val handlerCreator: HandlerCreator, - initialActions: List = emptyList(), + val initialActions: List = emptyList(), + val disposeActions: List = emptyList() ) : ViewModel(), Store, HandlerStore { private val _event: MutableSharedFlow = MutableSharedFlow() @@ -57,10 +60,6 @@ open class BaseStore, TComponent : Component> : KoinScopeComponent { + val loadModule: Boolean + get() = false + + val bindWithLifecycle: Boolean + get() = true + val module: Module @Composable diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/NavComponentScreen.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/NavComponentScreen.kt index dbb0eb8..7a8f159 100644 --- a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/NavComponentScreen.kt +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/NavComponentScreen.kt @@ -14,8 +14,11 @@ fun , TComponent : Component> NavComponentS component: TComponent, content: @Composable (TProcessor) -> Unit ) { - rememberKoinModules(unloadModules = true) { - listOf(feature.module) + if (feature.loadModule) { + rememberKoinModules(unloadModules = true) { + listOf(feature.module) + } } + content(feature.processor(component)) } diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/StoreExt.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/StoreExt.kt index c2fd960..a9c0c5e 100644 --- a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/StoreExt.kt +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/StoreExt.kt @@ -4,7 +4,7 @@ import com.stslex.atten.core.ui.mvi.handler.HandlerStore import com.stslex.atten.core.ui.mvi.Store.Action import com.stslex.atten.core.ui.mvi.Store.Event import com.stslex.atten.core.ui.mvi.Store.State -import com.stslex.wizard.core.ui.mvi.v2.Handler +import com.stslex.atten.core.ui.mvi.handler.Handler fun > Handler.invoke( store: HStore, diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/Handler.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/Handler.kt index e1986d0..251b944 100644 --- a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/Handler.kt +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/Handler.kt @@ -1,6 +1,5 @@ -package com.stslex.wizard.core.ui.mvi.v2 +package com.stslex.atten.core.ui.mvi.handler -import com.stslex.atten.core.ui.mvi.handler.HandlerStore import com.stslex.atten.core.ui.mvi.Store.Action fun interface Handler> { diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/HandlerCreator.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/HandlerCreator.kt index eeeb159..59ed496 100644 --- a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/HandlerCreator.kt +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/handler/HandlerCreator.kt @@ -1,6 +1,5 @@ -package com.stslex.wizard.core.ui.mvi.v2 +package com.stslex.atten.core.ui.mvi.handler -import com.stslex.atten.core.ui.mvi.handler.HandlerStore import com.stslex.atten.core.ui.mvi.Store.Action import com.stslex.atten.core.ui.mvi.Store.Event import com.stslex.atten.core.ui.mvi.Store.State diff --git a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/StoreProcessor.kt b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/StoreProcessor.kt index e64f2d2..8b6cc20 100644 --- a/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/StoreProcessor.kt +++ b/core/ui/mvi/src/commonMain/kotlin/com/stslex/atten/core/ui/mvi/processor/StoreProcessor.kt @@ -1,13 +1,14 @@ package com.stslex.atten.core.ui.mvi.processor import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.Immutable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember +import com.stslex.atten.core.ui.mvi.BaseStore import com.stslex.atten.core.ui.mvi.Store.Action import com.stslex.atten.core.ui.mvi.Store.Event import com.stslex.atten.core.ui.mvi.Store.State -import com.stslex.atten.core.ui.mvi.BaseStore import com.stslex.atten.core.ui.navigation.Component import com.stslex.wizard.core.ui.mvi.v2.processor.ActionProcessor import com.stslex.wizard.core.ui.mvi.v2.processor.EffectsProcessor @@ -60,6 +61,12 @@ inline fun + +/** + * DetailsFeature is a Koin feature module that provides the DetailsStore processor. + * It is responsible for managing the state and actions related to the profile feature. + * + * @see [com.stslex.atten.feature.details.ui.mvi.DetailsStore] + * */ +internal object DetailsFeature : Feature { + + override val module: Module by lazy { ModuleFeatureDetails().module } + + private val scopeName = requireNotNull(DetailsScope::class.qualifiedName) { + "Scope name is null. Please check the DetailsFeature class." + } + + override val scope: Scope by lazy { + getKoin().getOrCreateScope( + scopeId = scopeName, + qualifier = qualifier(scopeName) + ) + } + + @Composable + override fun processor( + component: DetailsComponent + ): DetailsStoreProcessor = rememberStoreProcessor(component) +} diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/DetailsScope.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/DetailsScope.kt new file mode 100644 index 0000000..a70e37b --- /dev/null +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/DetailsScope.kt @@ -0,0 +1,6 @@ +package com.stslex.atten.feature.details.di + +import org.koin.core.annotation.Scope + +@Scope(DetailsScope::class) +class DetailsScope \ No newline at end of file diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt deleted file mode 100644 index f01a2a6..0000000 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/FeatureDetailsModule.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.stslex.atten.feature.details.di - -import com.stslex.atten.core.core.coroutine.dispatcher.AppDispatcher -import com.stslex.atten.core.todo.data.repository.ToDoRepository -import com.stslex.atten.feature.details.domain.interactor.DetailsInteractor -import com.stslex.atten.feature.details.domain.interactor.DetailsInteractorImpl -import com.stslex.atten.feature.details.navigation.DetailsRouter -import com.stslex.atten.feature.details.ui.store.DetailsStore -import com.stslex.atten.feature.details.ui.store.DetailsStoreComponent -import org.koin.android.annotation.KoinViewModel -import org.koin.core.annotation.ComponentScan -import org.koin.core.annotation.Factory -import org.koin.core.annotation.Module - -@Module -@ComponentScan("com.stslex.atten.feature.details") -class FeatureDetailsModule { - - @Factory - fun interactor( - repository: ToDoRepository - ): DetailsInteractor = DetailsInteractorImpl(repository) - - @KoinViewModel - fun store( - interactor: DetailsInteractor, - appDispatcher: AppDispatcher, - router: DetailsRouter, - ): DetailsStore = DetailsStore(interactor, appDispatcher, router) - - @Factory - fun router(): DetailsRouter = object : DetailsRouter { - override fun invoke(event: DetailsStoreComponent.Navigation) { - TODO("Not yet implemented") - } - } -} diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/ModuleFeatureDetails.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/ModuleFeatureDetails.kt new file mode 100644 index 0000000..8fd565e --- /dev/null +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/di/ModuleFeatureDetails.kt @@ -0,0 +1,8 @@ +package com.stslex.atten.feature.details.di + +import org.koin.core.annotation.ComponentScan +import org.koin.core.annotation.Module + +@Module +@ComponentScan("com.stslex.atten.feature.details") +class ModuleFeatureDetails diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/domain/interactor/DetailsInteractorImpl.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/domain/interactor/DetailsInteractorImpl.kt index 95764d0..8814d7f 100644 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/domain/interactor/DetailsInteractorImpl.kt +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/domain/interactor/DetailsInteractorImpl.kt @@ -1,11 +1,18 @@ package com.stslex.atten.feature.details.domain.interactor import com.stslex.atten.core.todo.data.repository.ToDoRepository +import com.stslex.atten.feature.details.di.DetailsScope import com.stslex.atten.feature.details.domain.model.ToDoDetailsDomainModel import com.stslex.atten.feature.details.domain.model.ToDoDomainUpdateModel import com.stslex.atten.feature.details.domain.model.toDataModel import com.stslex.atten.feature.details.domain.model.toDomain +import org.koin.core.annotation.Factory +import org.koin.core.annotation.Scope +import org.koin.core.annotation.Scoped +@Factory +@Scope(DetailsScope::class) +@Scoped class DetailsInteractorImpl( private val repository: ToDoRepository ) : DetailsInteractor { diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsGraph.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsGraph.kt deleted file mode 100644 index 02129a0..0000000 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsGraph.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.stslex.atten.feature.details.navigation - -//fun NavGraphBuilder.detailsGraph() { -// navScreen { args -> -// val store = getStore() -// -// DisposableEffect(Unit) { -// store.dispatch(Action.Init(args.id)) -// onDispose { -// store.dispatch(Action.OnScreenLeft) -// } -// } -// -// val state by remember(store) { store.state }.collectAsState() -// -// DetailsScreen( -// state = state, -// onTitleChange = { store.dispatch(Action.OnTitleValueChanged(it)) }, -// onDescriptionChange = { store.dispatch(Action.OnDescriptionValueChanged(it)) }, -// onSaveClicked = { store.dispatch(Action.OnSaveClicked) } -// ) -// } -//} diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsRouter.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsRouter.kt deleted file mode 100644 index cc0a37a..0000000 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/navigation/DetailsRouter.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.stslex.atten.feature.details.navigation - -import com.arkivanov.decompose.ComponentContext -import com.stslex.atten.core.ui.kit.mvi.Router -import com.stslex.atten.feature.details.ui.mvi.DetailsComponent -import com.stslex.atten.feature.details.ui.store.DetailsStoreComponent.Navigation - -interface DetailsRouter : Router - -class DetailsComponentImpl( - private val popBack: () -> Unit, - componentContext: ComponentContext, -) : DetailsComponent, ComponentContext by componentContext { - -// override fun invoke(event: Navigation) { -// when (event) { -// Navigation.Back -> navigator.popBack() -// } -// } -} \ No newline at end of file diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/DetailsScreen.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/DetailsScreen.kt index 804a9d6..cbc6150 100644 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/DetailsScreen.kt +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/DetailsScreen.kt @@ -1,78 +1,21 @@ package com.stslex.atten.feature.details.ui -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.systemBarsPadding -import androidx.compose.material3.Button -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import com.stslex.atten.core.ui.kit.theme.AppDimension -import com.stslex.atten.core.ui.kit.theme.AppTheme -import com.stslex.atten.feature.details.ui.components.DescriptionTextField -import com.stslex.atten.feature.details.ui.components.TitleTextField -import com.stslex.atten.feature.details.ui.model.ToDoDetailsUIModel -import com.stslex.atten.feature.details.ui.store.DetailsStoreComponent.State -import com.stslex.atten.feature.details.ui.store.ScreenState -import org.jetbrains.compose.ui.tooling.preview.Preview +import com.stslex.atten.core.ui.mvi.NavComponentScreen +import com.stslex.atten.feature.details.di.DetailsFeature +import com.stslex.atten.feature.details.ui.mvi.DetailsComponent +import com.stslex.atten.feature.details.ui.mvi.DetailsStore.Action @Composable -internal fun DetailsScreen( - state: State, - onTitleChange: (String) -> Unit, - onDescriptionChange: (String) -> Unit, - onSaveClicked: () -> Unit, - modifier: Modifier = Modifier +fun DetailsScreen( + component: DetailsComponent ) { - Column( - modifier = modifier - .fillMaxSize() - .background(MaterialTheme.colorScheme.background) - .systemBarsPadding() - .padding(AppDimension.Padding.medium) - ) { - TitleTextField( - title = state.item.title, - onTitleChange = onTitleChange - ) - Spacer(modifier = Modifier.height(AppDimension.Padding.medium)) - DescriptionTextField( - modifier = Modifier.weight(1f), - description = state.item.description, - onDescriptionChange = onDescriptionChange - ) - Button( - onClick = onSaveClicked, - modifier = Modifier.align(Alignment.CenterHorizontally) - ) { - Text("Save") - } - } -} - -@Composable -@Preview -internal fun DetailsScreenPreview() { - AppTheme { - DetailsScreen( - state = State( - item = ToDoDetailsUIModel( - uuid = "uuid", - title = "Title", - description = "Description" - ), - screen = ScreenState.Content, - createdAt = null - ), - onTitleChange = {}, - onDescriptionChange = {}, - onSaveClicked = {} + NavComponentScreen(DetailsFeature, component) { processor -> + DetailsWidget( + state = processor.state.value, + onTitleChange = { processor.consume(Action.Input.Title(it)) }, + onDescriptionChange = { processor.consume(Action.Input.Description(it)) }, + onSaveClicked = { processor.consume(Action.Click.OnSaveClicked) } ) } } \ No newline at end of file diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/DetailsWidget.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/DetailsWidget.kt new file mode 100644 index 0000000..af8671b --- /dev/null +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/DetailsWidget.kt @@ -0,0 +1,78 @@ +package com.stslex.atten.feature.details.ui + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.material3.Button +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import com.stslex.atten.core.ui.kit.theme.AppDimension +import com.stslex.atten.core.ui.kit.theme.AppTheme +import com.stslex.atten.feature.details.ui.components.DescriptionTextField +import com.stslex.atten.feature.details.ui.components.TitleTextField +import com.stslex.atten.feature.details.ui.model.ToDoDetailsUIModel +import com.stslex.atten.feature.details.ui.mvi.DetailsStore +import com.stslex.atten.feature.details.ui.mvi.ScreenState +import org.jetbrains.compose.ui.tooling.preview.Preview + +@Composable +internal fun DetailsWidget( + state: DetailsStore.State, + onTitleChange: (String) -> Unit, + onDescriptionChange: (String) -> Unit, + onSaveClicked: () -> Unit, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background) + .systemBarsPadding() + .padding(AppDimension.Padding.medium) + ) { + TitleTextField( + title = state.item.title, + onTitleChange = onTitleChange + ) + Spacer(modifier = Modifier.height(AppDimension.Padding.medium)) + DescriptionTextField( + modifier = Modifier.weight(1f), + description = state.item.description, + onDescriptionChange = onDescriptionChange + ) + Button( + onClick = onSaveClicked, + modifier = Modifier.align(Alignment.CenterHorizontally) + ) { + Text("Save") + } + } +} + +@Composable +@Preview +internal fun DetailsWidgetPreview() { + AppTheme { + DetailsWidget( + state = DetailsStore.State( + item = ToDoDetailsUIModel( + uuid = "uuid", + title = "Title", + description = "Description" + ), + screen = ScreenState.Content, + createdAt = null + ), + onTitleChange = {}, + onDescriptionChange = {}, + onSaveClicked = {} + ) + } +} \ No newline at end of file diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsComponent.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsComponent.kt index 0b668a6..0a8ab5f 100644 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsComponent.kt +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsComponent.kt @@ -1,5 +1,25 @@ package com.stslex.atten.feature.details.ui.mvi +import com.arkivanov.decompose.ComponentContext import com.stslex.atten.core.ui.navigation.Component +import com.stslex.atten.feature.details.ui.mvi.DetailsStore.Action +import com.stslex.atten.feature.details.ui.mvi.handlers.DetailsComponentImpl +import com.stslex.atten.core.ui.mvi.handler.Handler -interface DetailsComponent : Component \ No newline at end of file +interface DetailsComponent : Component, Handler { + + val uuid: String + + companion object { + + fun create( + popBack: () -> Unit, + uuid: String, + componentContext: ComponentContext + ): DetailsComponent = DetailsComponentImpl( + componentContext = componentContext, + popBack = popBack, + uuid = uuid + ) + } +} \ No newline at end of file diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsHandlerStore.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsHandlerStore.kt new file mode 100644 index 0000000..7c328cf --- /dev/null +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsHandlerStore.kt @@ -0,0 +1,8 @@ +package com.stslex.atten.feature.details.ui.mvi + +import com.stslex.atten.core.ui.mvi.handler.HandlerStore +import com.stslex.atten.feature.details.ui.mvi.DetailsStore.Action +import com.stslex.atten.feature.details.ui.mvi.DetailsStore.Event +import com.stslex.atten.feature.details.ui.mvi.DetailsStore.State + +interface DetailsHandlerStore : HandlerStore, DetailsStore \ No newline at end of file diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsStore.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsStore.kt new file mode 100644 index 0000000..6d80680 --- /dev/null +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsStore.kt @@ -0,0 +1,85 @@ +package com.stslex.atten.feature.details.ui.mvi + +import androidx.compose.runtime.Stable +import com.stslex.atten.core.ui.mvi.CommonEvents +import com.stslex.atten.core.ui.mvi.Store +import com.stslex.atten.feature.details.ui.model.ToDoDetailsUIModel +import com.stslex.atten.feature.details.ui.mvi.DetailsStore.Action +import com.stslex.atten.feature.details.ui.mvi.DetailsStore.Event +import com.stslex.atten.feature.details.ui.mvi.DetailsStore.State +import kotlinx.datetime.LocalDateTime + +interface DetailsStore : Store { + + @Stable + data class State( + val item: ToDoDetailsUIModel, + val screen: ScreenState, + val createdAt: LocalDateTime? + ) : Store.State { + + companion object { + + val INIT = State( + item = ToDoDetailsUIModel( + uuid = "", + title = "", + description = "", + ), + screen = ScreenState.Shimmer, + createdAt = null + ) + } + } + + @Stable + sealed interface Event : Store.Event { + + @Stable + data class Snackbar( + val snackbar: CommonEvents.Snackbar + ) : Event + } + + @Stable + sealed interface Action : Store.Action { + + @Stable + sealed interface Input : Action { + + @Stable + data class Title(val title: String) : Input + + @Stable + data class Description(val description: String) : Input + } + + @Stable + sealed interface Common : Action { + + @Stable + data class InitialLoad(val uuid: String) : Common + + @Stable + data object OnScreenLeft : Common + + @Stable + data class OnError(val error: Throwable) : Common + } + + @Stable + sealed interface Click : Action { + + @Stable + data object OnSaveClicked : Click + + @Stable + data object OnBackClicked : Click + } + + sealed interface Navigation : Action, Store.Action.Navigation { + + data object Back : Navigation + } + } +} \ No newline at end of file diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsStoreImpl.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsStoreImpl.kt new file mode 100644 index 0000000..375349c --- /dev/null +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/DetailsStoreImpl.kt @@ -0,0 +1,42 @@ +package com.stslex.atten.feature.details.ui.mvi + +import com.stslex.atten.core.core.coroutine.dispatcher.AppDispatcher +import com.stslex.atten.core.ui.mvi.BaseStore +import com.stslex.atten.feature.details.di.DetailsScope +import com.stslex.atten.feature.details.ui.mvi.DetailsStore.Action +import com.stslex.atten.feature.details.ui.mvi.DetailsStore.Event +import com.stslex.atten.feature.details.ui.mvi.DetailsStore.State +import com.stslex.atten.feature.details.ui.mvi.handlers.ClickHandler +import com.stslex.atten.feature.details.ui.mvi.handlers.CommonHandler +import com.stslex.atten.feature.details.ui.mvi.handlers.InputHandler +import org.koin.android.annotation.KoinViewModel +import org.koin.core.annotation.InjectedParam +import org.koin.core.annotation.Qualifier +import org.koin.core.annotation.Scope +import org.koin.core.annotation.Scoped + +@KoinViewModel +@Scope(DetailsScope::class) +@Scoped +@Qualifier(DetailsScope::class) +internal class DetailsStoreImpl( + @InjectedParam component: DetailsComponent, + appDispatcher: AppDispatcher, + clickHandler: ClickHandler, + commonHandler: CommonHandler, + inputHandler: InputHandler +) : DetailsHandlerStore, BaseStore( + name = "DETAILS", + appDispatcher = appDispatcher, + initialState = State.INIT, + handlerCreator = { action -> + when (action) { + is Action.Click -> clickHandler + is Action.Common -> commonHandler + is Action.Input -> inputHandler + is Action.Navigation -> component + } + }, + initialActions = listOf(Action.Common.InitialLoad(component.uuid)), + disposeActions = listOf(Action.Common.OnScreenLeft) +) \ No newline at end of file diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/ScreenState.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/ScreenState.kt similarity index 83% rename from feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/ScreenState.kt rename to feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/ScreenState.kt index c09da9f..5111289 100644 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/ScreenState.kt +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/ScreenState.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.feature.details.ui.store +package com.stslex.atten.feature.details.ui.mvi import androidx.compose.runtime.Stable diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/handlers/ClickHandler.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/handlers/ClickHandler.kt new file mode 100644 index 0000000..ac53272 --- /dev/null +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/handlers/ClickHandler.kt @@ -0,0 +1,59 @@ +package com.stslex.atten.feature.details.ui.mvi.handlers + +import com.stslex.atten.core.ui.mvi.handler.Handler +import com.stslex.atten.feature.details.di.DetailsScope +import com.stslex.atten.feature.details.domain.interactor.DetailsInteractor +import com.stslex.atten.feature.details.ui.model.toDomain +import com.stslex.atten.feature.details.ui.mvi.DetailsHandlerStore +import com.stslex.atten.feature.details.ui.mvi.DetailsStore.Action +import org.koin.core.annotation.Factory +import org.koin.core.annotation.Scope +import org.koin.core.annotation.Scoped + +@Factory +@Scope(DetailsScope::class) +@Scoped +internal class ClickHandler( + private val interactor: DetailsInteractor, +) : Handler { + + override fun DetailsHandlerStore.invoke(action: Action.Click) { + when (action) { + Action.Click.OnBackClicked -> actionOnBackClicked() + Action.Click.OnSaveClicked -> actionOnSaveClicked() + } + } + + private fun DetailsHandlerStore.actionOnBackClicked() { + if ( + state.value.item.title.isEmpty() && + state.value.item.description.isEmpty() + ) { + launch( + action = { + interactor.removeItem(state.value.item.uuid) + }, + eachDispatcher = appDispatcher.immediate, + onSuccess = { consume(Action.Navigation.Back) }, + onError = { error -> consume(Action.Common.OnError(error)) } + ) + } else { + consume(Action.Navigation.Back) + } + } + + private fun DetailsHandlerStore.actionOnSaveClicked() { + launch( + action = { + state.value.let { currentState -> + val item = currentState.item.toDomain(currentState.createdAt) + ?: throw IllegalStateException("current created at is invalid") + interactor.updateItem(item) + } + }, + eachDispatcher = appDispatcher.immediate, + onSuccess = { consume(Action.Navigation.Back) }, + onError = { error -> consume(Action.Common.OnError(error)) } + ) + } +} \ No newline at end of file diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/handlers/CommonHandler.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/handlers/CommonHandler.kt new file mode 100644 index 0000000..0a60f4e --- /dev/null +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/handlers/CommonHandler.kt @@ -0,0 +1,86 @@ +package com.stslex.atten.feature.details.ui.mvi.handlers + +import com.stslex.atten.core.core.logger.Log +import com.stslex.atten.core.ui.mvi.CommonEvents.Snackbar +import com.stslex.atten.feature.details.di.DetailsScope +import com.stslex.atten.feature.details.domain.interactor.DetailsInteractor +import com.stslex.atten.feature.details.ui.model.toDomain +import com.stslex.atten.feature.details.ui.model.toUi +import com.stslex.atten.feature.details.ui.mvi.DetailsHandlerStore +import com.stslex.atten.feature.details.ui.mvi.DetailsStore.Action +import com.stslex.atten.feature.details.ui.mvi.DetailsStore.Event +import com.stslex.atten.feature.details.ui.mvi.ScreenState +import com.stslex.atten.core.ui.mvi.handler.Handler +import org.koin.core.annotation.Factory +import org.koin.core.annotation.Scope +import org.koin.core.annotation.Scoped + +@Factory +@Scope(DetailsScope::class) +@Scoped +internal class CommonHandler( + private val interactor: DetailsInteractor +) : Handler { + + override fun DetailsHandlerStore.invoke(action: Action.Common) { + when (action) { + is Action.Common.InitialLoad -> actionInit(action) + is Action.Common.OnScreenLeft -> actionOnScreenLeft() + is Action.Common.OnError -> actionError(action) + } + } + + private fun DetailsHandlerStore.actionInit(action: Action.Common.InitialLoad) { + launch( + action = { + interactor.getItem(action.uuid) ?: throw IllegalStateException("Item not found") + }, + onSuccess = { item -> + updateState { currentState -> + currentState.copy( + item = item.toUi(), + createdAt = item.createdAt + ) + } + }, + onError = { error -> consume(Action.Common.OnError(error)) } + ) + } + + + private fun DetailsHandlerStore.actionOnScreenLeft() { + launch( + action = { + state.value.let { currentState -> + if ( + state.value.item.title.isEmpty() && + state.value.item.description.isEmpty() + ) { + interactor.removeItem(state.value.item.uuid) + } else { + val item = currentState.item.toDomain(currentState.createdAt) + ?: throw IllegalStateException("current created at is invalid") + interactor.updateItem(item) + } + } + }, + onSuccess = { Log.d("Item updated") }, + onError = { error -> consume(Action.Common.OnError(error)) } + ) + } + + + private fun DetailsHandlerStore.actionError(action: Action.Common.OnError) { + val message = action.error.message ?: "Unknown error" + // todo: implement + if (state.value.screen is ScreenState.Content) { + sendEvent(Event.Snackbar(Snackbar.Error(message))) + } else { + updateState { currentState -> + currentState.copy( + screen = ScreenState.Error(message) + ) + } + } + } +} \ No newline at end of file diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/handlers/DetailsComponentImpl.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/handlers/DetailsComponentImpl.kt new file mode 100644 index 0000000..caef102 --- /dev/null +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/handlers/DetailsComponentImpl.kt @@ -0,0 +1,19 @@ +package com.stslex.atten.feature.details.ui.mvi.handlers + +import com.arkivanov.decompose.ComponentContext +import com.stslex.atten.feature.details.ui.mvi.DetailsComponent +import com.stslex.atten.feature.details.ui.mvi.DetailsHandlerStore +import com.stslex.atten.feature.details.ui.mvi.DetailsStore + +internal class DetailsComponentImpl( + componentContext: ComponentContext, + private val popBack: () -> Unit, + override val uuid: String +) : DetailsComponent, ComponentContext by componentContext { + + override fun DetailsHandlerStore.invoke(action: DetailsStore.Action.Navigation) { + when (action) { + DetailsStore.Action.Navigation.Back -> popBack() + } + } +} \ No newline at end of file diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/handlers/InputHandler.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/handlers/InputHandler.kt new file mode 100644 index 0000000..0cb8e64 --- /dev/null +++ b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/mvi/handlers/InputHandler.kt @@ -0,0 +1,47 @@ +package com.stslex.atten.feature.details.ui.mvi.handlers + +import com.stslex.atten.feature.details.di.DetailsScope +import com.stslex.atten.feature.details.ui.mvi.DetailsHandlerStore +import com.stslex.atten.feature.details.ui.mvi.DetailsStore.Action.Input +import com.stslex.atten.core.ui.mvi.handler.Handler +import org.koin.core.annotation.Factory +import org.koin.core.annotation.Scope +import org.koin.core.annotation.Scoped + +@Factory +@Scope(DetailsScope::class) +@Scoped +internal class InputHandler : Handler { + + override fun DetailsHandlerStore.invoke(action: Input) { + when (action) { + is Input.Description -> actionOnDescriptionValueChanged(action) + is Input.Title -> actionOnTitleValueChanged(action) + } + } + + private fun DetailsHandlerStore.actionOnDescriptionValueChanged(action: Input.Description) { + updateState { currentState -> + currentState.copy( + item = currentState.item.copy( + description = action.description + ) + ) + } + } + + private fun DetailsHandlerStore.actionOnTitleValueChanged(action: Input.Title) { + updateState { currentState -> + currentState.copy( + item = currentState.item.copy( + title = action.title.take(MAX_TITLE_LENGTH) + ) + ) + } + } + + companion object { + + private const val MAX_TITLE_LENGTH = 50 + } +} \ No newline at end of file diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStore.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStore.kt deleted file mode 100644 index 3ff5926..0000000 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStore.kt +++ /dev/null @@ -1,164 +0,0 @@ -package com.stslex.atten.feature.details.ui.store - -import com.stslex.atten.core.core.coroutine.dispatcher.AppDispatcher -import com.stslex.atten.core.core.logger.Log -import com.stslex.atten.core.ui.kit.mvi.Store -import com.stslex.atten.core.ui.kit.mvi.StoreComponent.Event.Snackbar -import com.stslex.atten.feature.details.domain.interactor.DetailsInteractor -import com.stslex.atten.feature.details.navigation.DetailsRouter -import com.stslex.atten.feature.details.ui.model.toDomain -import com.stslex.atten.feature.details.ui.model.toUi -import com.stslex.atten.feature.details.ui.store.DetailsStoreComponent.Action -import com.stslex.atten.feature.details.ui.store.DetailsStoreComponent.Event -import com.stslex.atten.feature.details.ui.store.DetailsStoreComponent.Navigation -import com.stslex.atten.feature.details.ui.store.DetailsStoreComponent.State -import kotlinx.coroutines.withContext - -class DetailsStore( - private val interactor: DetailsInteractor, - private val appDispatcher: AppDispatcher, - router: DetailsRouter -) : Store( - router = router, - initialState = State.INIT, - appDispatcher = appDispatcher -) { - - override fun process(action: Action) { - when (action) { - is Action.Init -> actionInit(action) - Action.OnBackClicked -> actionOnBackClicked() - is Action.OnDescriptionValueChanged -> actionOnDescriptionValueChanged(action) - is Action.OnTitleValueChanged -> actionOnTitleValueChanged(action) - is Action.OnScreenLeft -> actionOnScreenLeft() - is Action.OnSaveClicked -> actionOnSaveClicked() - } - } - - private fun actionOnSaveClicked() { - launch( - action = { - state.value.let { currentState -> - val item = currentState.item.toDomain(currentState.createdAt) - ?: throw IllegalStateException("current created at is invalid") - interactor.updateItem(item) - } - }, - onSuccess = { - withContext(appDispatcher.main.immediate) { - consumeNavigation(Navigation.Back) - } - }, - onError = { error -> - showError(error) - } - ) - } - - private fun actionInit(action: Action.Init) { - launch( - action = { - interactor.getItem(action.uuid) ?: throw IllegalStateException("Item not found") - }, - onSuccess = { item -> - updateState { currentState -> - currentState.copy( - item = item.toUi(), - createdAt = item.createdAt - ) - } - }, - onError = { error -> - showError(error) - } - ) - } - - private fun actionOnScreenLeft() { - launch( - action = { - state.value.let { currentState -> - if ( - state.value.item.title.isEmpty() && - state.value.item.description.isEmpty() - ) { - interactor.removeItem(state.value.item.uuid) - } else { - val item = currentState.item.toDomain(currentState.createdAt) - ?: throw IllegalStateException("current created at is invalid") - interactor.updateItem(item) - } - } - }, - onSuccess = { - Log.d("Item updated") - }, - onError = { error -> - showError(error) - } - ) - } - - private fun actionOnBackClicked() { - if ( - state.value.item.title.isEmpty() && - state.value.item.description.isEmpty() - ) { - launch( - action = { - interactor.removeItem(state.value.item.uuid) - }, - onSuccess = { - withContext(appDispatcher.main.immediate) { - consumeNavigation(Navigation.Back) - } - }, - onError = { error -> - showError(error) - } - ) - } else { - consumeNavigation(Navigation.Back) - } - } - - private fun actionOnDescriptionValueChanged(action: Action.OnDescriptionValueChanged) { - updateState { currentState -> - currentState.copy( - item = currentState.item.copy( - description = action.description - ) - ) - } - } - - private fun actionOnTitleValueChanged(action: Action.OnTitleValueChanged) { - updateState { currentState -> - currentState.copy( - item = currentState.item.copy( - title = action.title.take(MAX_TITLE_LENGTH) - ) - ) - } - } - - private fun showError(error: Throwable) { - val message = error.message ?: "Unknown error" - // todo: implement - if (state.value.screen is ScreenState.Content) { - sendEvent( - Event.Snackbar(Snackbar.Error(message)) - ) - } else { - updateState { currentState -> - currentState.copy( - screen = ScreenState.Error(message) - ) - } - } - } - - companion object { - private const val MAX_TITLE_LENGTH = 50 - } -} \ No newline at end of file diff --git a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStoreComponent.kt b/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStoreComponent.kt deleted file mode 100644 index 884058b..0000000 --- a/feature/details/src/commonMain/kotlin/com.stslex.atten.feature.details/ui/store/DetailsStoreComponent.kt +++ /dev/null @@ -1,65 +0,0 @@ -package com.stslex.atten.feature.details.ui.store - -import androidx.compose.runtime.Stable -import com.stslex.atten.core.ui.kit.mvi.StoreComponent -import com.stslex.atten.feature.details.ui.model.ToDoDetailsUIModel -import kotlinx.datetime.LocalDateTime - -interface DetailsStoreComponent : StoreComponent { - - @Stable - data class State( - val item: ToDoDetailsUIModel, - val screen: ScreenState, - val createdAt: LocalDateTime? - ) : StoreComponent.State { - - companion object { - val INIT = State( - item = ToDoDetailsUIModel( - uuid = "", - title = "", - description = "", - ), - screen = ScreenState.Shimmer, - createdAt = null - ) - } - } - - @Stable - sealed interface Event : StoreComponent.Event { - - @Stable - data class Snackbar( - val snackbar: StoreComponent.Event.Snackbar - ) : Event - } - - @Stable - sealed interface Action : StoreComponent.Action { - - @Stable - data class Init(val uuid: String) : Action - - @Stable - data object OnBackClicked : Action - - @Stable - data class OnTitleValueChanged(val title: String) : Action - - @Stable - data class OnDescriptionValueChanged(val description: String) : Action - - @Stable - data object OnScreenLeft : Action - - @Stable - data object OnSaveClicked : Action - } - - sealed interface Navigation : StoreComponent.Navigation { - - data object Back : Navigation - } -} \ No newline at end of file diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/HomeFeature.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/HomeFeature.kt new file mode 100644 index 0000000..1c4768b --- /dev/null +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/HomeFeature.kt @@ -0,0 +1,43 @@ +package com.stslex.atten.feature.home.di + +import androidx.compose.runtime.Composable +import com.stslex.atten.core.ui.mvi.Feature +import com.stslex.atten.core.ui.mvi.processor.StoreProcessor +import com.stslex.atten.core.ui.mvi.processor.rememberStoreProcessor +import com.stslex.atten.feature.home.ui.mvi.HomeComponent +import com.stslex.atten.feature.home.ui.mvi.HomeStore.Action +import com.stslex.atten.feature.home.ui.mvi.HomeStore.Event +import com.stslex.atten.feature.home.ui.mvi.HomeStore.State +import org.koin.core.module.Module +import org.koin.core.qualifier.qualifier +import org.koin.core.scope.Scope +import org.koin.ksp.generated.module + +internal typealias HomeStoreProcessor = StoreProcessor + +/** + * HomeFeature is a Koin feature module that provides the HomeStore processor. + * It is responsible for managing the state and actions related to the profile feature. + * + * @see [com.stslex.atten.feature.home.ui.mvi.HomeStore] + * */ +internal object HomeFeature : Feature { + + override val module: Module by lazy { ModuleFeatureHome().module } + + private val scopeName = requireNotNull(HomeScope::class.qualifiedName) { + "Scope name is null. Please check the HomeFeature class." + } + + override val scope: Scope by lazy { + getKoin().getOrCreateScope( + scopeId = scopeName, + qualifier = qualifier(scopeName) + ) + } + + @Composable + override fun processor( + component: HomeComponent + ): HomeStoreProcessor = rememberStoreProcessor(component) +} diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/HomeScope.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/HomeScope.kt new file mode 100644 index 0000000..0f73592 --- /dev/null +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/HomeScope.kt @@ -0,0 +1,6 @@ +package com.stslex.atten.feature.home.di + +import org.koin.core.annotation.Scope + +@Scope(HomeScope::class) +class HomeScope \ No newline at end of file diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt index bde79f6..a0e466d 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/di/ModuleFeatureHome.kt @@ -1,38 +1,9 @@ package com.stslex.atten.feature.home.di -import com.stslex.atten.core.core.coroutine.dispatcher.AppDispatcher -import com.stslex.atten.core.todo.data.repository.ToDoRepository -import com.stslex.atten.feature.home.domain.interactor.HomeScreenInteractor -import com.stslex.atten.feature.home.domain.interactor.HomeScreenInteractorImpl -import com.stslex.atten.feature.home.navigation.HomeRouter -import com.stslex.atten.feature.home.ui.store.HomeStore -import com.stslex.atten.feature.home.ui.store.HomeStoreComponent -import org.koin.android.annotation.KoinViewModel import org.koin.core.annotation.ComponentScan -import org.koin.core.annotation.Factory import org.koin.core.annotation.Module @Module @ComponentScan("com.stslex.atten.feature.home") -class ModuleFeatureHome { - - @Factory - fun interactor( - repository: ToDoRepository - ): HomeScreenInteractor = HomeScreenInteractorImpl(repository) - - @KoinViewModel - fun store( - interactor: HomeScreenInteractor, - appDispatcher: AppDispatcher, - router: HomeRouter, - ): HomeStore = HomeStore(interactor, appDispatcher, router) - - @Factory - fun router(): HomeRouter = object : HomeRouter { - override fun invoke(event: HomeStoreComponent.Navigation) { - TODO("Not yet implemented") - } - } -} +class ModuleFeatureHome diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/domain/interactor/HomeScreenInteractorImpl.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/domain/interactor/HomeScreenInteractorImpl.kt index 456c6da..fd06eac 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/domain/interactor/HomeScreenInteractorImpl.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/domain/interactor/HomeScreenInteractorImpl.kt @@ -6,6 +6,7 @@ import com.stslex.atten.core.paging.states.PagerLoadState import com.stslex.atten.core.paging.states.PagingState import com.stslex.atten.core.paging.states.pagingMap import com.stslex.atten.core.todo.data.repository.ToDoRepository +import com.stslex.atten.feature.home.di.HomeScope import com.stslex.atten.feature.home.domain.model.CreateTodoDomainModel import com.stslex.atten.feature.home.domain.model.ToDoDomainModel import com.stslex.atten.feature.home.domain.model.toData @@ -14,7 +15,13 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map +import org.koin.core.annotation.Factory +import org.koin.core.annotation.Scope +import org.koin.core.annotation.Scoped +@Factory +@Scope(HomeScope::class) +@Scoped class HomeScreenInteractorImpl( private val repository: ToDoRepository, ) : HomeScreenInteractor { diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeGraph.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeGraph.kt deleted file mode 100644 index cf8a4f6..0000000 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeGraph.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.stslex.atten.feature.home.navigation - -//fun NavGraphBuilder.homeGraph() { -// navScreen { -// val store = getStore() -// val state by remember { store.state }.collectAsState() -// val hapticFeedback = LocalHapticFeedback.current -// -// LaunchedEffect(Unit) { -// store.dispatch(Action.Init) -// -// store.event.collectLatest { -// when (it) { -// is Event.Snackbar -> { -// // show snackbar -// } -// } -// } -// } -// todo: add back handler -// AppBackHandler( -// enabled = state.selectedItems.isNotEmpty(), -// onBack = { -// store.dispatch(Action.OnBackPressed) -// } -// ) - -// HomeScreen( -// state = state, -// onItemClicked = { id -> -// hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove) -// store.dispatch(Action.OnItemClicked(id)) -// }, -// onLoadNext = { -// store.dispatch(Action.LoadMore) -// }, -// onCreateItemClick = { -// hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove) -// store.dispatch(Action.OnCreateItemClicked) -// }, -// onDeleteItemsClick = { -// hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove) -// store.dispatch(Action.OnDeleteItemsClicked) -// }, -// onItemLongCLick = { id -> -// hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) -// store.dispatch(Action.OnSelectItemClicked(id)) -// } -// ) -// } -//} diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouter.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouter.kt deleted file mode 100644 index f6018ce..0000000 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/navigation/HomeRouter.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.stslex.atten.feature.home.navigation - -import com.stslex.atten.core.ui.kit.mvi.Router -import com.stslex.atten.feature.home.ui.store.HomeStoreComponent.Navigation - -interface HomeRouter : Router diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/HomeScreen.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/HomeScreen.kt index 6488041..712ac27 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/HomeScreen.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/HomeScreen.kt @@ -1,167 +1,60 @@ package com.stslex.atten.feature.home.ui -import androidx.compose.animation.AnimatedContent -import androidx.compose.animation.core.animateDpAsState -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.animation.scaleIn -import androidx.compose.animation.scaleOut -import androidx.compose.animation.togetherWith -import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.IntrinsicSize -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.systemBarsPadding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.wrapContentSize -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Create -import androidx.compose.material.icons.filled.Delete -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import com.stslex.atten.core.paging.model.PagingConfig -import com.stslex.atten.core.paging.states.PagingUiState -import com.stslex.atten.core.paging.ui.PagingColumn -import com.stslex.atten.core.ui.kit.components.CardWithAnimatedBorder -import com.stslex.atten.core.ui.kit.theme.AppDimension -import com.stslex.atten.core.ui.kit.theme.AppTheme -import com.stslex.atten.feature.home.ui.components.HomeScreenItem -import com.stslex.atten.feature.home.ui.model.TodoUiModel -import com.stslex.atten.feature.home.ui.store.HomeStoreComponent.State -import com.stslex.atten.feature.home.ui.store.ScreenState -import kotlinx.collections.immutable.persistentSetOf -import kotlinx.collections.immutable.toImmutableList -import org.jetbrains.compose.ui.tooling.preview.Preview +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalHapticFeedback +import com.stslex.atten.core.ui.mvi.NavComponentScreen +import com.stslex.atten.feature.home.di.HomeFeature +import com.stslex.atten.feature.home.ui.mvi.HomeComponent +import com.stslex.atten.feature.home.ui.mvi.HomeStore +import com.stslex.atten.feature.home.ui.mvi.HomeStore.Action -@OptIn(ExperimentalFoundationApi::class) @Composable -internal fun HomeScreen( - state: State, - onItemLongCLick: (id: String) -> Unit, - onItemClicked: (id: String) -> Unit, - onLoadNext: () -> Unit, - onCreateItemClick: () -> Unit, - onDeleteItemsClick: () -> Unit, - modifier: Modifier = Modifier, -) { - Box( - modifier = modifier - .fillMaxSize() - .background(MaterialTheme.colorScheme.background) - .systemBarsPadding(), - ) { - PagingColumn( - modifier = Modifier - .fillMaxSize() - .padding( - horizontal = AppDimension.Padding.medium, - ), - pagingState = state.paging, - onLoadNext = onLoadNext - ) { - items( - count = state.paging.items.size, - key = state.paging.key - ) { index -> - state.paging.items.getOrNull(index)?.let { item -> - HomeScreenItem( - modifier = Modifier.animateItem(), - item = item, - onItemClick = onItemClicked, - onItemLongClick = onItemLongCLick - ) - } - Spacer(modifier = Modifier.height(AppDimension.Padding.medium)) - } - } +fun HomeScreen(component: HomeComponent) { + NavComponentScreen(HomeFeature, component) { processor -> + val hapticFeedback = LocalHapticFeedback.current - val buttonShapeRadius by animateDpAsState( - targetValue = if (state.selectedItems.isNotEmpty()) { - AppDimension.Radius.largest - } else { - AppDimension.Radius.medium - } - ) + processor.handle { event -> + when (event) { + is HomeStore.Event.Snackbar -> { - CardWithAnimatedBorder( - modifier = Modifier - .align(Alignment.BottomEnd) - .padding(AppDimension.Padding.big) - .wrapContentSize() - .width(IntrinsicSize.Max) - .height(IntrinsicSize.Max), - onClick = { - if (state.selectedItems.isNotEmpty()) { - onDeleteItemsClick() - } else { - onCreateItemClick() } - }, - isAnimated = state.selectedItems.isNotEmpty(), - cornerRadius = buttonShapeRadius, - backgroundColor = MaterialTheme.colorScheme.secondaryContainer, - disableBorderColor = MaterialTheme.colorScheme.onSecondaryContainer - ) { - AnimatedContent( - modifier = Modifier.padding(AppDimension.Padding.big), - targetState = state.selectedItems.isNotEmpty(), - transitionSpec = { - fadeIn().plus(scaleIn()) togetherWith - fadeOut().plus(scaleOut()) + + HomeStore.Event.Haptic.Click -> { + hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove) + } + + HomeStore.Event.Haptic.LongClick -> { + hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress) } - ) { isDeleting -> - Icon( - imageVector = if (isDeleting) { - Icons.Filled.Delete - } else { - Icons.Filled.Create - }, - contentDescription = null, - tint = MaterialTheme.colorScheme.onSecondaryContainer - ) } } - } -} -@Composable -@Preview -internal fun HomeScreenPreview() { - AppTheme { - AppTheme { - HomeScreen( - state = State( - query = "", - paging = PagingUiState( - items = Array(10) { - TodoUiModel( - uuid = "UniqueKey $it", - title = "Title $it", - description = "Description $it", - isSelected = false - ) - }.toList().toImmutableList(), - hasMore = true, - total = 100, - config = PagingConfig.DEFAULT - ), - screen = ScreenState.Content.Data, - selectedItems = persistentSetOf() - ), - onLoadNext = {}, - onItemClicked = {}, - onCreateItemClick = {}, - onItemLongCLick = {}, - onDeleteItemsClick = {} - ) - } + // todo: add back handler +// AppBackHandler( +// enabled = state.selectedItems.isNotEmpty(), +// onBack = { +// store.dispatch(Action.OnBackPressed) +// } +// ) + + HomeWidget( + state = processor.state.value, + onItemClicked = { id -> + processor.consume(Action.Click.OnItemClicked(id)) + }, + onLoadNext = { + processor.consume(Action.Paging.LoadMore) + }, + onCreateItemClick = { + processor.consume(Action.Click.OnCreateItemClicked) + }, + onDeleteItemsClick = { + processor.consume(Action.Click.OnDeleteItemsClicked) + }, + onItemLongCLick = { id -> + processor.consume(Action.Click.OnSelectItemClicked(id)) + } + ) } } \ No newline at end of file diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/HomeWidget.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/HomeWidget.kt new file mode 100644 index 0000000..c8cf53d --- /dev/null +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/HomeWidget.kt @@ -0,0 +1,165 @@ +package com.stslex.atten.feature.home.ui + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.scaleIn +import androidx.compose.animation.scaleOut +import androidx.compose.animation.togetherWith +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Create +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import com.stslex.atten.core.paging.model.PagingConfig +import com.stslex.atten.core.paging.states.PagingUiState +import com.stslex.atten.core.paging.ui.PagingColumn +import com.stslex.atten.core.ui.kit.components.CardWithAnimatedBorder +import com.stslex.atten.core.ui.kit.theme.AppDimension +import com.stslex.atten.core.ui.kit.theme.AppTheme +import com.stslex.atten.feature.home.ui.components.HomeScreenItem +import com.stslex.atten.feature.home.ui.model.TodoUiModel +import com.stslex.atten.feature.home.ui.mvi.HomeStore.State +import com.stslex.atten.feature.home.ui.mvi.ScreenState +import kotlinx.collections.immutable.persistentSetOf +import kotlinx.collections.immutable.toImmutableList +import org.jetbrains.compose.ui.tooling.preview.Preview + +@OptIn(ExperimentalFoundationApi::class) +@Composable +internal fun HomeWidget( + state: State, + onItemLongCLick: (id: String) -> Unit, + onItemClicked: (id: String) -> Unit, + onLoadNext: () -> Unit, + onCreateItemClick: () -> Unit, + onDeleteItemsClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Box( + modifier = modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background) + .systemBarsPadding(), + ) { + PagingColumn( + modifier = Modifier + .fillMaxSize() + .padding( + horizontal = AppDimension.Padding.medium, + ), + pagingState = state.paging, + onLoadNext = onLoadNext + ) { + items( + count = state.paging.items.size, + key = state.paging.key + ) { index -> + state.paging.items.getOrNull(index)?.let { item -> + HomeScreenItem( + modifier = Modifier.animateItem(), + item = item, + onItemClick = onItemClicked, + onItemLongClick = onItemLongCLick + ) + } + Spacer(modifier = Modifier.height(AppDimension.Padding.medium)) + } + } + + val buttonShapeRadius by animateDpAsState( + targetValue = if (state.selectedItems.isNotEmpty()) { + AppDimension.Radius.largest + } else { + AppDimension.Radius.medium + } + ) + + CardWithAnimatedBorder( + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(AppDimension.Padding.big) + .wrapContentSize() + .width(IntrinsicSize.Max) + .height(IntrinsicSize.Max), + onClick = { + if (state.selectedItems.isNotEmpty()) { + onDeleteItemsClick() + } else { + onCreateItemClick() + } + }, + isAnimated = state.selectedItems.isNotEmpty(), + cornerRadius = buttonShapeRadius, + backgroundColor = MaterialTheme.colorScheme.secondaryContainer, + disableBorderColor = MaterialTheme.colorScheme.onSecondaryContainer + ) { + AnimatedContent( + modifier = Modifier.padding(AppDimension.Padding.big), + targetState = state.selectedItems.isNotEmpty(), + transitionSpec = { + fadeIn().plus(scaleIn()) togetherWith + fadeOut().plus(scaleOut()) + } + ) { isDeleting -> + Icon( + imageVector = if (isDeleting) { + Icons.Filled.Delete + } else { + Icons.Filled.Create + }, + contentDescription = null, + tint = MaterialTheme.colorScheme.onSecondaryContainer + ) + } + } + } +} + +@Composable +@Preview +internal fun HomeScreenPreview() { + AppTheme { + HomeWidget( + state = State( + query = "", + paging = PagingUiState( + items = Array(10) { + TodoUiModel( + uuid = "UniqueKey $it", + title = "Title $it", + description = "Description $it", + isSelected = false + ) + }.toList().toImmutableList(), + hasMore = true, + total = 100, + config = PagingConfig.DEFAULT + ), + screen = ScreenState.Content.Data, + selectedItems = persistentSetOf() + ), + onLoadNext = {}, + onItemClicked = {}, + onCreateItemClick = {}, + onItemLongCLick = {}, + onDeleteItemsClick = {} + ) + } +} \ No newline at end of file diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeComponent.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeComponent.kt index 552cf6f..6fbce5b 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeComponent.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeComponent.kt @@ -1,5 +1,24 @@ package com.stslex.atten.feature.home.ui.mvi +import com.arkivanov.decompose.ComponentContext import com.stslex.atten.core.ui.navigation.Component +import com.stslex.atten.core.ui.navigation.Config +import com.stslex.atten.feature.home.ui.mvi.HomeStore.Action +import com.stslex.atten.feature.home.ui.mvi.handlers.HomeComponentImpl +import com.stslex.atten.core.ui.mvi.handler.Handler -interface HomeComponent : Component \ No newline at end of file +interface HomeComponent : Component, Handler { + + companion object { + + fun create( + popBack: () -> Unit, + navTo: (config: Config) -> Unit, + componentContext: ComponentContext + ): HomeComponent = HomeComponentImpl( + popBack = popBack, + navTo = navTo, + componentContext = componentContext + ) + } +} \ No newline at end of file diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeHandlerStore.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeHandlerStore.kt new file mode 100644 index 0000000..96390f3 --- /dev/null +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeHandlerStore.kt @@ -0,0 +1,8 @@ +package com.stslex.atten.feature.home.ui.mvi + +import com.stslex.atten.core.ui.mvi.handler.HandlerStore +import com.stslex.atten.feature.home.ui.mvi.HomeStore.Action +import com.stslex.atten.feature.home.ui.mvi.HomeStore.Event +import com.stslex.atten.feature.home.ui.mvi.HomeStore.State + +interface HomeHandlerStore : HandlerStore, HomeStore \ No newline at end of file diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeRouterImpl.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeRouterImpl.kt deleted file mode 100644 index ef810ae..0000000 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeRouterImpl.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.stslex.atten.feature.home.ui.mvi - -import com.arkivanov.decompose.ComponentContext -import com.stslex.atten.core.ui.navigation.Component -import com.stslex.atten.feature.home.ui.store.HomeStoreComponent - -class HomeComponentImpl( - private val popBack: () -> Unit, - private val navTo: () -> Unit, - componentContext: ComponentContext -) : HomeComponent, ComponentContext by componentContext { - -// override fun invoke(event: HomeStoreComponent.Navigation) { -// when (event) { -// is HomeStoreComponent.Navigation.NavigateToDetail -> navigator.navigateTo( -// Component.DetailScreen( -// event.id -// ) -// ) -// -// is HomeStoreComponent.Navigation.Back -> navigator.popBack() -// } -// } -} \ No newline at end of file diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeStore.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeStore.kt new file mode 100644 index 0000000..06a9a5f --- /dev/null +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeStore.kt @@ -0,0 +1,111 @@ +package com.stslex.atten.feature.home.ui.mvi + +import androidx.compose.runtime.Stable +import com.stslex.atten.core.paging.model.PagingConfig +import com.stslex.atten.core.paging.states.PagingUiState +import com.stslex.atten.core.ui.mvi.CommonEvents +import com.stslex.atten.core.ui.mvi.Store +import com.stslex.atten.feature.home.ui.model.TodoUiModel +import com.stslex.atten.feature.home.ui.mvi.HomeStore.Action +import com.stslex.atten.feature.home.ui.mvi.HomeStore.Event +import com.stslex.atten.feature.home.ui.mvi.HomeStore.State +import kotlinx.collections.immutable.ImmutableSet +import kotlinx.collections.immutable.persistentSetOf + +interface HomeStore : Store { + + @Stable + data class State( + val query: String, + val paging: PagingUiState, + val screen: ScreenState, + val selectedItems: ImmutableSet + ) : Store.State { + + companion object { + + val INIT = State( + query = "", + paging = PagingUiState.default( + PagingConfig( + pageSize = 30, + pageOffset = 1f + ) + ), + screen = ScreenState.Shimmer, + selectedItems = persistentSetOf() + ) + } + } + + @Stable + sealed interface Event : Store.Event { + + @Stable + data class Snackbar( + val snackbar: CommonEvents.Snackbar + ) : Event + + sealed interface Haptic : Event { + + data object Click : Haptic + + data object LongClick : Haptic + } + } + + @Stable + sealed interface Action : Store.Action { + + sealed interface Paging : Action { + + @Stable + data object Init : Paging + + @Stable + data object LoadMore : Paging + + @Stable + data object Refresh : Paging + + @Stable + data object Retry : Paging + } + + @Stable + sealed interface Common : Action { + + @Stable + data class ProcessError(val error: Throwable) : Common + } + + @Stable + sealed interface Click : Action { + + @Stable + data object OnBackPressed : Click + + @Stable + data class OnItemClicked(val id: String) : Click + + @Stable + data object OnDeleteItemsClicked : Click + + @Stable + data class OnSelectItemClicked(val id: String) : Click + + @Stable + data object OnCreateItemClicked : Click + } + + @Stable + sealed interface Navigation : Action, Store.Action.Navigation { + + @Stable + data class NavigateToDetail(val id: String) : Navigation + + @Stable + data object Back : Navigation + } + } +} \ No newline at end of file diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeStoreImpl.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeStoreImpl.kt new file mode 100644 index 0000000..611b4d8 --- /dev/null +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/HomeStoreImpl.kt @@ -0,0 +1,43 @@ +package com.stslex.atten.feature.home.ui.mvi + +import com.stslex.atten.core.core.coroutine.dispatcher.AppDispatcher +import com.stslex.atten.core.ui.mvi.BaseStore +import com.stslex.atten.feature.home.di.HomeScope +import com.stslex.atten.feature.home.ui.mvi.HomeStore.Action +import com.stslex.atten.feature.home.ui.mvi.HomeStore.Event +import com.stslex.atten.feature.home.ui.mvi.HomeStore.State +import com.stslex.atten.feature.home.ui.mvi.handlers.ClickHandler +import com.stslex.atten.feature.home.ui.mvi.handlers.CommonHandler +import com.stslex.atten.feature.home.ui.mvi.handlers.PagingHandler +import org.koin.android.annotation.KoinViewModel +import org.koin.core.annotation.InjectedParam +import org.koin.core.annotation.Qualifier +import org.koin.core.annotation.Scope +import org.koin.core.annotation.Scoped + +@KoinViewModel +@Scope(HomeScope::class) +@Scoped +@Qualifier(HomeScope::class) +internal class HomeStoreImpl( + @InjectedParam component: HomeComponent, + appDispatcher: AppDispatcher, + clickHandler: ClickHandler, + pagingHandler: PagingHandler, + commonHandler: CommonHandler +) : HomeHandlerStore, BaseStore( + name = "HOME", + initialState = State.INIT, + appDispatcher = appDispatcher, + handlerCreator = { action -> + when (action) { + is Action.Click -> clickHandler + is Action.Navigation -> component + is Action.Paging -> pagingHandler + is Action.Common -> commonHandler + } + }, + initialActions = listOf( + Action.Paging.Init + ) +) \ No newline at end of file diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/ScreenState.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/ScreenState.kt similarity index 95% rename from feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/ScreenState.kt rename to feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/ScreenState.kt index 4bdc811..b854f2f 100644 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/ScreenState.kt +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/ScreenState.kt @@ -1,4 +1,4 @@ -package com.stslex.atten.feature.home.ui.store +package com.stslex.atten.feature.home.ui.mvi import androidx.compose.runtime.Stable import com.stslex.atten.core.paging.states.PagerLoadState diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/handlers/ClickHandler.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/handlers/ClickHandler.kt new file mode 100644 index 0000000..8a6e016 --- /dev/null +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/handlers/ClickHandler.kt @@ -0,0 +1,106 @@ +package com.stslex.atten.feature.home.ui.mvi.handlers + +import com.stslex.atten.feature.home.di.HomeScope +import com.stslex.atten.feature.home.domain.interactor.HomeScreenInteractor +import com.stslex.atten.feature.home.domain.model.CreateTodoDomainModel +import com.stslex.atten.feature.home.ui.mvi.HomeHandlerStore +import com.stslex.atten.feature.home.ui.mvi.HomeStore.Action +import com.stslex.atten.feature.home.ui.mvi.HomeStore.Event +import com.stslex.atten.core.ui.mvi.handler.Handler +import kotlinx.collections.immutable.persistentSetOf +import kotlinx.collections.immutable.toImmutableSet +import org.koin.core.annotation.Factory +import org.koin.core.annotation.Scope +import org.koin.core.annotation.Scoped + +@Factory +@Scope(HomeScope::class) +@Scoped +internal class ClickHandler( + private val interactor: HomeScreenInteractor +) : Handler { + + override fun HomeHandlerStore.invoke(action: Action.Click) { + when (action) { + is Action.Click.OnBackPressed -> actionOnBackPressed() + is Action.Click.OnCreateItemClicked -> actionOnCreateItemClicked() + is Action.Click.OnDeleteItemsClicked -> actionOnDeleteItemClicked() + is Action.Click.OnItemClicked -> actionOnItemClicked(action) + is Action.Click.OnSelectItemClicked -> actionOnSelectItemClicked(action) + } + } + + private fun HomeHandlerStore.actionOnItemClicked( + action: Action.Click.OnItemClicked + ) { + if (state.value.selectedItems.isNotEmpty()) { + consume(Action.Click.OnSelectItemClicked(action.id)) + } else { + sendEvent(Event.Haptic.Click) + consume(Action.Navigation.NavigateToDetail(action.id)) + } + } + + private fun HomeHandlerStore.actionOnCreateItemClicked() { + sendEvent(Event.Haptic.Click) + launch( + action = { + interactor.createItem(CreateTodoDomainModel(title = "", description = "")) + }, + onSuccess = { item -> + if (item == null) { + consume(Action.Common.ProcessError(IllegalStateException("Item is null"))) + } else { + consume(Action.Navigation.NavigateToDetail(item.uuid)) + } + }, + onError = { consume(Action.Common.ProcessError(it)) } + ) + } + + private fun HomeHandlerStore.actionOnBackPressed() { + sendEvent(Event.Haptic.Click) + if (state.value.selectedItems.isNotEmpty()) { + updateState { currentState -> + currentState.copy( + selectedItems = persistentSetOf() + ) + } + } else { + consume(Action.Navigation.Back) + } + } + + private fun HomeHandlerStore.actionOnDeleteItemClicked() { + sendEvent(Event.Haptic.Click) + launch( + action = { + interactor.deleteItems(state.value.selectedItems) + }, + onSuccess = { + // todo add success orientation + updateState { currentState -> + currentState.copy(selectedItems = persistentSetOf()) + } + }, + onError = { consume(Action.Common.ProcessError(it)) } + ) + } + + private fun HomeHandlerStore.actionOnSelectItemClicked( + action: Action.Click.OnSelectItemClicked + ) { + sendEvent(Event.Haptic.LongClick) + updateState { currentState -> + val selectedItems = currentState.selectedItems.toMutableSet() + if (selectedItems.contains(action.id)) { + selectedItems.remove(action.id) + } else { + selectedItems.add(action.id) + } + currentState.copy( + selectedItems = selectedItems.toImmutableSet(), + ) + } + } +} \ No newline at end of file diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/handlers/CommonHandler.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/handlers/CommonHandler.kt new file mode 100644 index 0000000..63505c5 --- /dev/null +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/handlers/CommonHandler.kt @@ -0,0 +1,39 @@ +package com.stslex.atten.feature.home.ui.mvi.handlers + +import com.stslex.atten.core.ui.mvi.CommonEvents.Snackbar +import com.stslex.atten.feature.home.di.HomeScope +import com.stslex.atten.feature.home.ui.mvi.HomeHandlerStore +import com.stslex.atten.feature.home.ui.mvi.HomeStore.Action +import com.stslex.atten.feature.home.ui.mvi.HomeStore.Event +import com.stslex.atten.feature.home.ui.mvi.ScreenState +import com.stslex.atten.core.ui.mvi.handler.Handler +import org.koin.core.annotation.Factory +import org.koin.core.annotation.Scope +import org.koin.core.annotation.Scoped + +@Factory +@Scope(HomeScope::class) +@Scoped +internal class CommonHandler : Handler { + + override fun HomeHandlerStore.invoke(action: Action.Common) { + when (action) { + is Action.Common.ProcessError -> processError(action) + } + } + + private fun HomeHandlerStore.processError( + action: Action.Common.ProcessError + ) { + val message = action.error.message ?: "Unknown error" + if (state.value.screen is ScreenState.Content) { + sendEvent(Event.Snackbar(Snackbar.Error(message))) + } else { + updateState { currentState -> + currentState.copy( + screen = ScreenState.Error(message) + ) + } + } + } +} \ No newline at end of file diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/handlers/HomeComponentImpl.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/handlers/HomeComponentImpl.kt new file mode 100644 index 0000000..5fb6953 --- /dev/null +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/handlers/HomeComponentImpl.kt @@ -0,0 +1,21 @@ +package com.stslex.atten.feature.home.ui.mvi.handlers + +import com.arkivanov.decompose.ComponentContext +import com.stslex.atten.core.ui.navigation.Config +import com.stslex.atten.feature.home.ui.mvi.HomeComponent +import com.stslex.atten.feature.home.ui.mvi.HomeHandlerStore +import com.stslex.atten.feature.home.ui.mvi.HomeStore.Action + +internal class HomeComponentImpl( + private val popBack: () -> Unit, + private val navTo: (config: Config) -> Unit, + componentContext: ComponentContext +) : HomeComponent, ComponentContext by componentContext { + + override fun HomeHandlerStore.invoke(action: Action.Navigation) { + when (action) { + is Action.Navigation.Back -> popBack() + is Action.Navigation.NavigateToDetail -> navTo(Config.Detail(action.id)) + } + } +} \ No newline at end of file diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/handlers/PagingHandler.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/handlers/PagingHandler.kt new file mode 100644 index 0000000..16926b2 --- /dev/null +++ b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/mvi/handlers/PagingHandler.kt @@ -0,0 +1,117 @@ +package com.stslex.atten.feature.home.ui.mvi.handlers + +import com.stslex.atten.core.paging.states.PagerAction +import com.stslex.atten.core.paging.states.PagerLoadState +import com.stslex.atten.core.paging.states.pagingMap +import com.stslex.atten.core.paging.states.toUi +import com.stslex.atten.core.ui.mvi.CommonEvents +import com.stslex.atten.feature.home.di.HomeScope +import com.stslex.atten.feature.home.domain.interactor.HomeScreenInteractor +import com.stslex.atten.feature.home.ui.model.toUi +import com.stslex.atten.feature.home.ui.mvi.HomeHandlerStore +import com.stslex.atten.feature.home.ui.mvi.HomeStore.Action +import com.stslex.atten.feature.home.ui.mvi.HomeStore.Event +import com.stslex.atten.feature.home.ui.mvi.toUi +import com.stslex.atten.core.ui.mvi.handler.Handler +import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map +import org.koin.core.annotation.Factory +import org.koin.core.annotation.Scope +import org.koin.core.annotation.Scoped + +@Factory +@Scope(HomeScope::class) +@Scoped +internal class PagingHandler( + private val interactor: HomeScreenInteractor +) : Handler { + + override fun HomeHandlerStore.invoke(action: Action.Paging) { + when (action) { + Action.Paging.Init -> actionInit() + Action.Paging.LoadMore -> actionLoadMore() + Action.Paging.Refresh -> actionRefresh() + Action.Paging.Retry -> actionRetry() + } + } + + private fun HomeHandlerStore.actionInit() { + interactor.pagingState + .map { pagingState -> + pagingState.pagingMap { it.toUi() } + } + .distinctUntilChanged() + .launch { pagerState -> + updateState { currentState -> + val pagingUiState = pagerState.toUi(currentState.paging.config) + currentState.copy( + paging = pagingUiState.copy( + items = pagingUiState.items + .map { item -> + item.copy( + isSelected = currentState.selectedItems.contains(item.uuid) + ) + } + .toImmutableList() + ) + ) + } + } + + interactor.pagingLoadState.launch { loadState -> + updateState { currentState -> + currentState.copy( + screen = loadState.toUi() + ) + } + } + + interactor.pagingEvents.launch { + sendEvent(Event.Snackbar(CommonEvents.Snackbar.Error("error load matches"))) + } + + state + .map { it.selectedItems } + .distinctUntilChanged() + .launch { selectedItems -> + updateState { currentState -> + currentState.copy( + paging = currentState.paging.copy( + items = currentState.paging.items + .map { item -> + item.copy(isSelected = selectedItems.contains(item.uuid)) + } + .toImmutableList() + ) + ) + } + } + + state + .map { it.query } + .distinctUntilChanged() + .launch( + onError = { consume(Action.Common.ProcessError(it)) } + ) { + if (interactor.pagingLoadState.value is PagerLoadState.Initial) { + interactor.process(PagerAction.Initial) + } else { + interactor.process(PagerAction.Load(isForce = false)) + } + } + } + + + private fun actionLoadMore() { + interactor.process(PagerAction.Load(isForce = false)) + } + + private fun actionRefresh() { + interactor.process(PagerAction.Refresh(isForce = true)) + } + + private fun actionRetry() { + interactor.process(PagerAction.Retry) + } +} \ No newline at end of file diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStore.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStore.kt deleted file mode 100644 index 932ccbe..0000000 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStore.kt +++ /dev/null @@ -1,220 +0,0 @@ -package com.stslex.atten.feature.home.ui.store - -import com.stslex.atten.core.core.coroutine.dispatcher.AppDispatcher -import com.stslex.atten.core.paging.states.PagerAction -import com.stslex.atten.core.paging.states.PagerLoadState -import com.stslex.atten.core.paging.states.pagingMap -import com.stslex.atten.core.paging.states.toUi -import com.stslex.atten.core.ui.kit.mvi.Store -import com.stslex.atten.core.ui.kit.mvi.StoreComponent.Event.Snackbar.Error -import com.stslex.atten.feature.home.domain.interactor.HomeScreenInteractor -import com.stslex.atten.feature.home.domain.model.CreateTodoDomainModel -import com.stslex.atten.feature.home.navigation.HomeRouter -import com.stslex.atten.feature.home.ui.model.toUi -import com.stslex.atten.feature.home.ui.store.HomeStoreComponent.Action -import com.stslex.atten.feature.home.ui.store.HomeStoreComponent.Event -import com.stslex.atten.feature.home.ui.store.HomeStoreComponent.Navigation -import com.stslex.atten.feature.home.ui.store.HomeStoreComponent.State -import kotlinx.collections.immutable.persistentSetOf -import kotlinx.collections.immutable.toImmutableList -import kotlinx.collections.immutable.toImmutableSet -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.withContext - -class HomeStore( - private val interactor: HomeScreenInteractor, - private val appDispatcher: AppDispatcher, - router: HomeRouter, -) : Store( - appDispatcher = appDispatcher, - initialState = State.INIT, - router = router -) { - - override fun process(action: Action) { - when (action) { - is Action.Init -> actionInit() - is Action.LoadMore -> actionLoadMore() - is Action.OnItemClicked -> actionOnItemClicked(action) - is Action.Refresh -> actionRefresh() - is Action.Retry -> actionRetry() - is Action.OnCreateItemClicked -> actionOnCreateItemClicked() - is Action.OnDeleteItemsClicked -> actionOnDeleteItemClicked() - is Action.OnSelectItemClicked -> actionOnSelectItemClicked(action) - is Action.OnBackPressed -> actionOnBackPressed() - } - } - - private fun actionOnBackPressed() { - if (state.value.selectedItems.isNotEmpty()) { - updateState { currentState -> - currentState.copy( - selectedItems = persistentSetOf() - ) - } - } else { - consumeNavigation(Navigation.Back) - } - } - - private fun actionInit() { - interactor.pagingState - .map { pagingState -> - pagingState.pagingMap { it.toUi() } - } - .distinctUntilChanged() - .launch { pagerState -> - updateState { currentState -> - val pagingUiState = pagerState.toUi(currentState.paging.config) - currentState.copy( - paging = pagingUiState.copy( - items = pagingUiState.items - .map { item -> - item.copy( - isSelected = currentState.selectedItems.contains(item.uuid) - ) - } - .toImmutableList() - ) - ) - } - } - - interactor.pagingLoadState.launch { loadState -> - updateState { currentState -> - currentState.copy( - screen = loadState.toUi() - ) - } - } - - interactor.pagingEvents.launch { - sendEvent( - Event.Snackbar(Error("error load matches")) - ) - } - - state - .map { it.selectedItems } - .distinctUntilChanged() - .launch { selectedItems -> - updateState { currentState -> - currentState.copy( - paging = currentState.paging.copy( - items = currentState.paging.items - .map { item -> - item.copy(isSelected = selectedItems.contains(item.uuid)) - } - .toImmutableList() - ) - ) - } - } - - state - .map { it.query } - .distinctUntilChanged() - .launch( - onError = ::showError - ) { - if (interactor.pagingLoadState.value is PagerLoadState.Initial) { - interactor.process(PagerAction.Initial) - } else { - interactor.process(PagerAction.Load(isForce = false)) - } - } - } - - private fun actionLoadMore() { - interactor.process(PagerAction.Load(isForce = false)) - } - - private fun actionRefresh() { - interactor.process(PagerAction.Refresh(isForce = true)) - } - - private fun actionRetry() { - interactor.process(PagerAction.Retry) - } - - private fun actionOnItemClicked(action: Action.OnItemClicked) { - if (state.value.selectedItems.isNotEmpty()) { - actionOnSelectItemClicked(Action.OnSelectItemClicked(action.id)) - } else { - consumeNavigation(Navigation.NavigateToDetail(action.id)) - } - } - - private fun actionOnCreateItemClicked() { - launch( - action = { - interactor - .createItem( - CreateTodoDomainModel( - title = "", - description = "" - ) - ) - }, - onSuccess = { item -> - item?.let { - withContext(appDispatcher.main.immediate) { - consumeNavigation(Navigation.NavigateToDetail(it.uuid)) - } - } - }, - onError = { - showError(it) - } - ) - } - - private fun actionOnDeleteItemClicked() { - launch( - action = { - interactor.deleteItems(state.value.selectedItems) - }, - onSuccess = { - // todo add success orientation - updateState { currentState -> - currentState.copy( - selectedItems = persistentSetOf() - ) - } - }, - onError = { - showError(it) - } - ) - } - - private fun actionOnSelectItemClicked(action: Action.OnSelectItemClicked) { - updateState { currentState -> - val selectedItems = currentState.selectedItems.toMutableSet() - if (selectedItems.contains(action.id)) { - selectedItems.remove(action.id) - } else { - selectedItems.add(action.id) - } - currentState.copy( - selectedItems = selectedItems.toImmutableSet(), - ) - } - } - - private fun showError(error: Throwable) { - val message = error.message ?: "Unknown error" - if (state.value.screen is ScreenState.Content) { - sendEvent( - Event.Snackbar(Error(message)) - ) - } else { - updateState { currentState -> - currentState.copy( - screen = ScreenState.Error(message) - ) - } - } - } -} \ No newline at end of file diff --git a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStoreComponent.kt b/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStoreComponent.kt deleted file mode 100644 index a77f376..0000000 --- a/feature/home/src/commonMain/kotlin/com.stslex.atten.feature.home/ui/store/HomeStoreComponent.kt +++ /dev/null @@ -1,85 +0,0 @@ -package com.stslex.atten.feature.home.ui.store - -import androidx.compose.runtime.Stable -import com.stslex.atten.core.paging.model.PagingConfig -import com.stslex.atten.core.paging.states.PagingUiState -import com.stslex.atten.core.ui.kit.mvi.StoreComponent -import com.stslex.atten.feature.home.ui.model.TodoUiModel -import kotlinx.collections.immutable.ImmutableSet -import kotlinx.collections.immutable.persistentSetOf - -interface HomeStoreComponent : StoreComponent { - - @Stable - data class State( - val query: String, - val paging: PagingUiState, - val screen: ScreenState, - val selectedItems: ImmutableSet - ) : StoreComponent.State { - - companion object { - val INIT = State( - query = "", - paging = PagingUiState.default( - PagingConfig( - pageSize = 30, - pageOffset = 1f - ) - ), - screen = ScreenState.Shimmer, - selectedItems = persistentSetOf() - ) - } - } - - @Stable - sealed interface Event : StoreComponent.Event { - - @Stable - data class Snackbar( - val snackbar: StoreComponent.Event.Snackbar - ) : Event - } - - @Stable - sealed interface Action : StoreComponent.Action { - - @Stable - data object Init : Action - - @Stable - data object LoadMore : Action - - @Stable - data class OnItemClicked(val id: String) : Action - - @Stable - data object OnDeleteItemsClicked : Action - - @Stable - data class OnSelectItemClicked(val id: String) : Action - - @Stable - data object OnCreateItemClicked : Action - - @Stable - data object Refresh : Action - - @Stable - data object Retry : Action - - @Stable - data object OnBackPressed : Action - } - - @Stable - sealed interface Navigation : StoreComponent.Navigation { - - @Stable - data class NavigateToDetail(val id: String) : Navigation - - @Stable - data object Back : Navigation - } -} \ No newline at end of file From cf1d6e8b53391798a2d07b32bb480fc51b6f5322 Mon Sep 17 00:00:00 2001 From: stslex Date: Mon, 30 Jun 2025 19:30:14 +0300 Subject: [PATCH 6/8] fix ci --- .github/workflows/build_mac_os.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build_mac_os.yml b/.github/workflows/build_mac_os.yml index 1527d50..349c595 100644 --- a/.github/workflows/build_mac_os.yml +++ b/.github/workflows/build_mac_os.yml @@ -33,5 +33,8 @@ jobs: LOCAL_PROPERTIES: ${{ secrets.LOCAL_PROPERTIES }} run: echo "$LOCAL_PROPERTIES" > ./local.properties + - name: generate Ksp metadata + run: ./gradlew kspKotlinMetadata + - name: Build with Gradle run: cd iosApp && xcodebuild -workspace ./iosApp.xcworkspace -scheme iosApp -configuration Debug -destination 'platform=iOS Simulator,OS=latest,name=iPhone 15' CODE_SIGNING_ALLOWED='NO' \ No newline at end of file From 279247300b47f5be8c0d9e0beb721db0c2df507c Mon Sep 17 00:00:00 2001 From: stslex Date: Mon, 30 Jun 2025 20:31:22 +0300 Subject: [PATCH 7/8] update pods --- iosApp/Podfile.lock | 2 +- iosApp/Pods/Manifest.lock | 2 +- iosApp/Pods/Pods.xcodeproj/project.pbxproj | 153 +++++++++--------- .../Pods-iosApp/Pods-iosApp.debug.xcconfig | 1 + .../Pods-iosApp/Pods-iosApp.release.xcconfig | 1 + 5 files changed, 84 insertions(+), 75 deletions(-) diff --git a/iosApp/Podfile.lock b/iosApp/Podfile.lock index 512df39..bed59fe 100644 --- a/iosApp/Podfile.lock +++ b/iosApp/Podfile.lock @@ -13,4 +13,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 5e5644ffb905eafaba6959582c3434bd5c2d1f31 -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/iosApp/Pods/Manifest.lock b/iosApp/Pods/Manifest.lock index 512df39..bed59fe 100644 --- a/iosApp/Pods/Manifest.lock +++ b/iosApp/Pods/Manifest.lock @@ -13,4 +13,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 5e5644ffb905eafaba6959582c3434bd5c2d1f31 -COCOAPODS: 1.15.2 +COCOAPODS: 1.16.2 diff --git a/iosApp/Pods/Pods.xcodeproj/project.pbxproj b/iosApp/Pods/Pods.xcodeproj/project.pbxproj index 41efe58..4a03689 100644 --- a/iosApp/Pods/Pods.xcodeproj/project.pbxproj +++ b/iosApp/Pods/Pods.xcodeproj/project.pbxproj @@ -20,13 +20,13 @@ /* End PBXAggregateTarget section */ /* Begin PBXBuildFile section */ - 000F6764CC3611909F5588C4DD324091 /* Pods-iosApp-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 70E8DFC7821955063C886C71258CBE53 /* Pods-iosApp-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 3ECE25B7023450AFB38D1C1068760386 /* Pods-iosApp-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BC3BD8CAFAE0C8EB92CD04E5FC24E61 /* Pods-iosApp-dummy.m */; }; - C82986791CF939F1BFA0A183A7160DF2 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */; }; + 4075C4E9655B3338183E38719ED9FE0D /* Pods-iosApp-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 70E8DFC7821955063C886C71258CBE53 /* Pods-iosApp-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 66DB69FF29A4FA55F6C8BB6CB40F491B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 384DDA2CB25005BD6479B5987C619DD4 /* Foundation.framework */; }; + EBCF850A151C6E7E1F90BFC12FD4CC83 /* Pods-iosApp-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 9BC3BD8CAFAE0C8EB92CD04E5FC24E61 /* Pods-iosApp-dummy.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ - 647FD4C4DC8F4287B9819258FCE0FFAD /* PBXContainerItemProxy */ = { + 11E94933F5512B8E41C533FE34AC2756 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; proxyType = 1; @@ -37,11 +37,11 @@ /* Begin PBXFileReference section */ 257390D34074D2442461A69FE6970CBD /* Pods-iosApp-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-iosApp-resources.sh"; sourceTree = ""; }; + 384DDA2CB25005BD6479B5987C619DD4 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 4D3E6DCB9CAB65A8A05C467E2BBC1F0D /* Pods-iosApp-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-iosApp-acknowledgements.markdown"; sourceTree = ""; }; 6094F47CD28E1D0CDB842E3689152602 /* commonApp.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = commonApp.framework; path = build/cocoapods/framework/commonApp.framework; sourceTree = ""; }; 6A3C5EB0586A09C512019B6B6A2DE103 /* Pods-iosApp-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-iosApp-Info.plist"; sourceTree = ""; }; 70E8DFC7821955063C886C71258CBE53 /* Pods-iosApp-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-iosApp-umbrella.h"; sourceTree = ""; }; - 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 9BC3BD8CAFAE0C8EB92CD04E5FC24E61 /* Pods-iosApp-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-iosApp-dummy.m"; sourceTree = ""; }; 9C49AEBC7AA7C80C03295804C6F07963 /* Pods-iosApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-iosApp.release.xcconfig"; sourceTree = ""; }; 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; @@ -56,11 +56,11 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 17D20EE9B9E5CEE47277F2FDB453040B /* Frameworks */ = { + 118FCDBC2D741D92D627022B8A69FDCE /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C82986791CF939F1BFA0A183A7160DF2 /* Foundation.framework in Frameworks */, + 66DB69FF29A4FA55F6C8BB6CB40F491B /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -110,14 +110,6 @@ path = "Target Support Files/Pods-iosApp"; sourceTree = ""; }; - 578452D2E740E91742655AC8F1636D1F /* iOS */ = { - isa = PBXGroup; - children = ( - 73010CC983E3809BECEE5348DA1BB8C6 /* Foundation.framework */, - ); - name = iOS; - sourceTree = ""; - }; 755A85A12736417D95BFCF4CAB738584 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -168,19 +160,27 @@ D210D550F4EA176C3123ED886F8F87F5 /* Frameworks */ = { isa = PBXGroup; children = ( - 578452D2E740E91742655AC8F1636D1F /* iOS */, + E4801F62A6B08CD9B5410329F1A18FDE /* iOS */, ); name = Frameworks; sourceTree = ""; }; + E4801F62A6B08CD9B5410329F1A18FDE /* iOS */ = { + isa = PBXGroup; + children = ( + 384DDA2CB25005BD6479B5987C619DD4 /* Foundation.framework */, + ); + name = iOS; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ - ECACD39E7139F61236E116CC2E146EAF /* Headers */ = { + EA4A762CE1D3907A8205BDE67070F468 /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 000F6764CC3611909F5588C4DD324091 /* Pods-iosApp-umbrella.h in Headers */, + 4075C4E9655B3338183E38719ED9FE0D /* Pods-iosApp-umbrella.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -189,17 +189,17 @@ /* Begin PBXNativeTarget section */ ED39C638569286489CD697A6C8964146 /* Pods-iosApp */ = { isa = PBXNativeTarget; - buildConfigurationList = 9E387E0E5841F9BB2027399BC86D473C /* Build configuration list for PBXNativeTarget "Pods-iosApp" */; + buildConfigurationList = D6CF5511E22381A5F90C955ED6D35BE5 /* Build configuration list for PBXNativeTarget "Pods-iosApp" */; buildPhases = ( - ECACD39E7139F61236E116CC2E146EAF /* Headers */, - 2E8732F641A2D00F489BFA7FFD57E4D3 /* Sources */, - 17D20EE9B9E5CEE47277F2FDB453040B /* Frameworks */, - E8F07320B073641AC684CCA20E85289F /* Resources */, + EA4A762CE1D3907A8205BDE67070F468 /* Headers */, + A878FD5F78AA8483FAA814B5BB5D11C4 /* Sources */, + 118FCDBC2D741D92D627022B8A69FDCE /* Frameworks */, + C4B054CEAB60328571B48A3E52F7B795 /* Resources */, ); buildRules = ( ); dependencies = ( - 8DBA9698B7BE43D55D3A928E1DD8AA9A /* PBXTargetDependency */, + 884785E0295CBDE2CEEAFD2012A98A24 /* PBXTargetDependency */, ); name = "Pods-iosApp"; productName = Pods_iosApp; @@ -212,8 +212,8 @@ BFDFE7DC352907FC980B868725387E98 /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1500; - LastUpgradeCheck = 1500; + LastSwiftUpdateCheck = 1600; + LastUpgradeCheck = 1600; }; buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; compatibilityVersion = "Xcode 9.3"; @@ -225,6 +225,7 @@ ); mainGroup = CF1408CF629C7361332E53B88F7BD30C; minimizedProjectReferenceProxies = 0; + preferredProjectObjectVersion = 77; productRefGroup = 1F86AA6785DF34AFD5A71790761717DE /* Products */; projectDirPath = ""; projectRoot = ""; @@ -236,7 +237,7 @@ /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - E8F07320B073641AC684CCA20E85289F /* Resources */ = { + C4B054CEAB60328571B48A3E52F7B795 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( @@ -259,61 +260,26 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 2E8732F641A2D00F489BFA7FFD57E4D3 /* Sources */ = { + A878FD5F78AA8483FAA814B5BB5D11C4 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 3ECE25B7023450AFB38D1C1068760386 /* Pods-iosApp-dummy.m in Sources */, + EBCF850A151C6E7E1F90BFC12FD4CC83 /* Pods-iosApp-dummy.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ - 8DBA9698B7BE43D55D3A928E1DD8AA9A /* PBXTargetDependency */ = { + 884785E0295CBDE2CEEAFD2012A98A24 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = commonApp; target = 60CD729CEF3BE74848D72FFA01C9B6A3 /* commonApp */; - targetProxy = 647FD4C4DC8F4287B9819258FCE0FFAD /* PBXContainerItemProxy */; + targetProxy = 11E94933F5512B8E41C533FE34AC2756 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ - 216BBF18FCF9A80E3F8B8B29DFDADEAF /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = AA1464A163D75B93E1F0C066308CA546 /* commonApp.debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CLANG_ENABLE_OBJC_WEAK = NO; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 2DF8E4235D00A0C8073049FDEBAA1F24 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = C6F99CDB9787D5217AAF36EE2319D997 /* commonApp.release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CLANG_ENABLE_OBJC_WEAK = NO; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; 30E0B9EFD9A5C45D0D351231E81B30B3 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { @@ -376,7 +342,7 @@ }; name = Release; }; - AA7BF216A54CB6899256610845D3F513 /* Release */ = { + 52E3856D68D29E1FCA4D74B3B1C3AD15 /* Release */ = { isa = XCBuildConfiguration; baseConfigurationReference = 9C49AEBC7AA7C80C03295804C6F07963 /* Pods-iosApp.release.xcconfig */; buildSettings = { @@ -390,6 +356,8 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; INFOPLIST_FILE = "Target Support Files/Pods-iosApp/Pods-iosApp-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 16.0; @@ -414,7 +382,25 @@ }; name = Release; }; - D2CF5CA5371D717A3073A132FE4EA080 /* Debug */ = { + 945C3677641DE2A20A7BFF820795A6F8 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AA1464A163D75B93E1F0C066308CA546 /* commonApp.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_OBJC_WEAK = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + B167DF5C9A7010E530279D48F3243AF6 /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = F981EE0C95E2DFD40CA16F05D2C35B8A /* Pods-iosApp.debug.xcconfig */; buildSettings = { @@ -428,6 +414,8 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; INFOPLIST_FILE = "Target Support Files/Pods-iosApp/Pods-iosApp-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 16.0; @@ -451,6 +439,25 @@ }; name = Debug; }; + D70A9625FA072F745652966B5FD51053 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = C6F99CDB9787D5217AAF36EE2319D997 /* commonApp.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_OBJC_WEAK = NO; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; F4FF6A0D1970CA9705974E3CB2134802 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -532,17 +539,17 @@ 786D7CE6BBA519E4B7B8E823702C6D6A /* Build configuration list for PBXAggregateTarget "commonApp" */ = { isa = XCConfigurationList; buildConfigurations = ( - 216BBF18FCF9A80E3F8B8B29DFDADEAF /* Debug */, - 2DF8E4235D00A0C8073049FDEBAA1F24 /* Release */, + 945C3677641DE2A20A7BFF820795A6F8 /* Debug */, + D70A9625FA072F745652966B5FD51053 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 9E387E0E5841F9BB2027399BC86D473C /* Build configuration list for PBXNativeTarget "Pods-iosApp" */ = { + D6CF5511E22381A5F90C955ED6D35BE5 /* Build configuration list for PBXNativeTarget "Pods-iosApp" */ = { isa = XCConfigurationList; buildConfigurations = ( - D2CF5CA5371D717A3073A132FE4EA080 /* Debug */, - AA7BF216A54CB6899256610845D3F513 /* Release */, + B167DF5C9A7010E530279D48F3243AF6 /* Debug */, + 52E3856D68D29E1FCA4D74B3B1C3AD15 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; diff --git a/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp.debug.xcconfig b/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp.debug.xcconfig index 23bb1e4..0b70b9f 100644 --- a/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp.debug.xcconfig +++ b/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp.debug.xcconfig @@ -4,6 +4,7 @@ FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/../../commonApp/build/cocoap GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -framework "commonApp" +OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/commonApp" PODS_BUILD_DIR = ${BUILD_DIR} PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_PODFILE_DIR_PATH = ${SRCROOT}/. diff --git a/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp.release.xcconfig b/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp.release.xcconfig index 23bb1e4..0b70b9f 100644 --- a/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp.release.xcconfig +++ b/iosApp/Pods/Target Support Files/Pods-iosApp/Pods-iosApp.release.xcconfig @@ -4,6 +4,7 @@ FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/../../commonApp/build/cocoap GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -framework "commonApp" +OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/commonApp" PODS_BUILD_DIR = ${BUILD_DIR} PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_PODFILE_DIR_PATH = ${SRCROOT}/. From b62829aa432f71ded0b9134c02cf6d8ad6959f12 Mon Sep 17 00:00:00 2001 From: stslex Date: Mon, 30 Jun 2025 21:07:15 +0300 Subject: [PATCH 8/8] fix gradle jsvm args for heap --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 7758ba4..17df7ab 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ #Gradle -org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M" +org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options=-Xmx2048M org.gradle.caching=true org.gradle.configuration-cache=true #Kotlin