From 0f1790081f25eff27cbde52eb0c0e00f291d7a19 Mon Sep 17 00:00:00 2001 From: stslex Date: Thu, 23 Nov 2023 21:36:38 +0300 Subject: [PATCH 1/3] initial setup --- composeApp/build.gradle.kts | 2 + composeApp/src/commonMain/kotlin/App.kt | 75 +--------------- composeApp/src/commonMain/kotlin/AppTheme.kt | 21 +++++ .../src/commonMain/kotlin/InitialApp.kt | 61 +++++++++++++ .../commonMain/kotlin/core/AppDispatcher.kt | 18 ++++ .../src/commonMain/kotlin/core/CommonUtils.kt | 7 ++ .../src/commonMain/kotlin/core/Logger.kt | 34 +++++++ .../kotlin/main_screen/MainScreen.kt | 24 +++++ .../src/commonMain/kotlin/store/BaseStore.kt | 89 +++++++++++++++++++ .../src/commonMain/kotlin/store/Router.kt | 5 ++ .../src/commonMain/kotlin/store/Store.kt | 12 +++ gradle/libs.versions.toml | 2 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 ++ settings.gradle.kts | 4 +- 14 files changed, 287 insertions(+), 75 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/AppTheme.kt create mode 100644 composeApp/src/commonMain/kotlin/InitialApp.kt create mode 100644 composeApp/src/commonMain/kotlin/core/AppDispatcher.kt create mode 100644 composeApp/src/commonMain/kotlin/core/CommonUtils.kt create mode 100644 composeApp/src/commonMain/kotlin/core/Logger.kt create mode 100644 composeApp/src/commonMain/kotlin/main_screen/MainScreen.kt create mode 100644 composeApp/src/commonMain/kotlin/store/BaseStore.kt create mode 100644 composeApp/src/commonMain/kotlin/store/Router.kt create mode 100644 composeApp/src/commonMain/kotlin/store/Store.kt create mode 100644 iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 8c9528e0..3d6e766d 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -41,6 +41,8 @@ kotlin { implementation(compose.desktop.currentOs) } commonMain.dependencies { + implementation(libs.kermit) + implementation(projects.shared) implementation(compose.runtime) implementation(compose.foundation) diff --git a/composeApp/src/commonMain/kotlin/App.kt b/composeApp/src/commonMain/kotlin/App.kt index 7bca07a8..73babd79 100644 --- a/composeApp/src/commonMain/kotlin/App.kt +++ b/composeApp/src/commonMain/kotlin/App.kt @@ -1,82 +1,9 @@ -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.material.Button -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.material.darkColors -import androidx.compose.material.lightColors import androidx.compose.runtime.Composable -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier import org.jetbrains.compose.resources.ExperimentalResourceApi -import org.jetbrains.compose.resources.painterResource -@OptIn(ExperimentalResourceApi::class) @Composable fun App() { AppTheme { - Box( - modifier = Modifier - .fillMaxSize() - .background(MaterialTheme.colors.background) - ) { - val defaultGreetState = "Hello World!" - val clickedGreetState = "Compose: ${Greeting().greet()}" - var isClicked by remember { mutableStateOf(false) } - val greetingText by remember { - derivedStateOf { - if (isClicked) { - clickedGreetState - } else { - defaultGreetState - } - } - } - Column( - modifier = Modifier.fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Button( - onClick = { - isClicked = !isClicked - } - ) { - Text(greetingText) - } - AnimatedVisibility(isClicked) { - Image( - painterResource("compose-multiplatform.xml"), - null - ) - } - } - } + InitialApp() } } - -@Composable -fun AppTheme( - isDarkTheme: Boolean = isSystemInDarkTheme(), - content: @Composable () -> Unit, -) { - val colors = if (isDarkTheme) { - darkColors() - } else { - lightColors() - } - MaterialTheme( - colors = colors, - content = content - ) -} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/AppTheme.kt b/composeApp/src/commonMain/kotlin/AppTheme.kt new file mode 100644 index 00000000..89e9d566 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/AppTheme.kt @@ -0,0 +1,21 @@ +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material.MaterialTheme +import androidx.compose.material.darkColors +import androidx.compose.material.lightColors +import androidx.compose.runtime.Composable + +@Composable +fun AppTheme( + isDarkTheme: Boolean = isSystemInDarkTheme(), + content: @Composable () -> Unit, +) { + val colors = if (isDarkTheme) { + darkColors() + } else { + lightColors() + } + MaterialTheme( + colors = colors, + content = content + ) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/InitialApp.kt b/composeApp/src/commonMain/kotlin/InitialApp.kt new file mode 100644 index 00000000..6a076584 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/InitialApp.kt @@ -0,0 +1,61 @@ +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material.Button +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import org.jetbrains.compose.resources.ExperimentalResourceApi +import org.jetbrains.compose.resources.painterResource + +@OptIn(ExperimentalResourceApi::class) +@Composable +fun InitialApp() { + Box( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colors.background) + ) { + val defaultGreetState = "Hello World!" + val clickedGreetState = "Compose: ${Greeting().greet()}" + var isClicked by remember { mutableStateOf(false) } + val greetingText by remember { + derivedStateOf { + if (isClicked) { + clickedGreetState + } else { + defaultGreetState + } + } + } + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Button( + onClick = { + isClicked = !isClicked + } + ) { + Text(greetingText) + } + AnimatedVisibility(isClicked) { + Image( + painterResource("compose-multiplatform.xml"), + null + ) + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/core/AppDispatcher.kt b/composeApp/src/commonMain/kotlin/core/AppDispatcher.kt new file mode 100644 index 00000000..973b9ddd --- /dev/null +++ b/composeApp/src/commonMain/kotlin/core/AppDispatcher.kt @@ -0,0 +1,18 @@ +package core + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO +import kotlinx.coroutines.MainCoroutineDispatcher + +interface AppDispatcher { + val io: CoroutineDispatcher + val main: MainCoroutineDispatcher + val default: CoroutineDispatcher +} + +class AppDispatcherImpl : AppDispatcher { + override val io: CoroutineDispatcher = Dispatchers.IO + override val main: MainCoroutineDispatcher = Dispatchers.Main + override val default: CoroutineDispatcher = Dispatchers.Default +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/core/CommonUtils.kt b/composeApp/src/commonMain/kotlin/core/CommonUtils.kt new file mode 100644 index 00000000..056973a2 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/core/CommonUtils.kt @@ -0,0 +1,7 @@ +package core + +import kotlinx.coroutines.CoroutineExceptionHandler + +val coroutineExceptionHandler = CoroutineExceptionHandler { _, throwable -> + Logger.exception(throwable) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/core/Logger.kt b/composeApp/src/commonMain/kotlin/core/Logger.kt new file mode 100644 index 00000000..5e04aca9 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/core/Logger.kt @@ -0,0 +1,34 @@ +package core + +import co.touchlab.kermit.Logger as Log + +object Logger { + + private const val DEFAULT_TAG = "WIZARD" + + fun exception( + throwable: Throwable, + tag: String? = null, + message: String? = null + ) { + // TODO check build config if (BuildConfig.DEBUG.not()) return + val currentTag = "$DEFAULT_TAG:${tag.orEmpty()}" + Log.e( + tag = currentTag, + throwable = throwable, + messageString = message ?: throwable.message.orEmpty(), + ) + } + + fun debug( + message: String, + tag: String? = null, + ) { + // TODO check build config if (BuildConfig.DEBUG.not()) return + val currentTag = "$DEFAULT_TAG:${tag.orEmpty()}" + Log.d( + tag = currentTag, + messageString = message + ) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/main_screen/MainScreen.kt b/composeApp/src/commonMain/kotlin/main_screen/MainScreen.kt new file mode 100644 index 00000000..e6443bfd --- /dev/null +++ b/composeApp/src/commonMain/kotlin/main_screen/MainScreen.kt @@ -0,0 +1,24 @@ +package main_screen + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier + +@Composable +fun MainScreen( + modifier: Modifier = Modifier +) { + Box( + modifier = modifier + .fillMaxSize() + .background(MaterialTheme.colors.background), + contentAlignment = Alignment.Center + ) { + Text(text = "Main Screen") + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/store/BaseStore.kt b/composeApp/src/commonMain/kotlin/store/BaseStore.kt new file mode 100644 index 00000000..c1892728 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/store/BaseStore.kt @@ -0,0 +1,89 @@ +package store + +import core.AppDispatcher +import core.Logger +import core.coroutineExceptionHandler +import kotlinx.coroutines.CoroutineExceptionHandler +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.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import st.slex.csplashscreen.core.ui.mvi.Router +import st.slex.csplashscreen.core.ui.mvi.Store.Action +import st.slex.csplashscreen.core.ui.mvi.Store.Event +import st.slex.csplashscreen.core.ui.mvi.Store.Navigation +import st.slex.csplashscreen.core.ui.mvi.Store.State + +abstract class BaseStore( + private val router: Router, + private val appDispatcher: AppDispatcher, + initialState: S +) { + + abstract fun sendAction(action: A) + + private val _state: MutableStateFlow = MutableStateFlow(initialState) + val state: StateFlow = _state.asStateFlow() + + private val scope: CoroutineScope = CoroutineScope(appDispatcher.default) + + private fun exceptionHandler( + onError: suspend (cause: Throwable) -> Unit = {}, + ) = CoroutineExceptionHandler { coroutineContext, throwable -> + Logger.exception(throwable) + CoroutineScope(coroutineContext).launch(coroutineExceptionHandler) { + onError(throwable) + } + } + + val event: MutableSharedFlow = MutableSharedFlow() + + fun updateState(update: (S) -> S) { + _state.update(update) + } + + fun sendEvent(event: E) { + scope.launch(appDispatcher.default) { + this@BaseStore.event.emit(event) + } + } + + fun navigate(event: N) { + router(event) + } + + fun launch( + onError: suspend (Throwable) -> Unit = {}, + block: suspend CoroutineScope.() -> Unit, + ): Job = scope.launch( + context = exceptionHandler(onError), + block = block + ) + + fun launch( + action: suspend CoroutineScope.() -> T, + onError: suspend (Throwable) -> Unit = {}, + onSuccess: (T) -> Unit, + ): Job = scope.launch( + context = exceptionHandler(onError), + block = { + onSuccess(action()) + } + ) + + fun Flow.launch( + onError: suspend (cause: Throwable) -> Unit = {}, + each: suspend (T) -> Unit + ): Job = this + .onEach(each) + .flowOn(exceptionHandler(onError)) + .launchIn(scope) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/store/Router.kt b/composeApp/src/commonMain/kotlin/store/Router.kt new file mode 100644 index 00000000..4763faa2 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/store/Router.kt @@ -0,0 +1,5 @@ +package st.slex.csplashscreen.core.ui.mvi + +fun interface Router { + operator fun invoke(event: E) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/store/Store.kt b/composeApp/src/commonMain/kotlin/store/Store.kt new file mode 100644 index 00000000..a3117d6b --- /dev/null +++ b/composeApp/src/commonMain/kotlin/store/Store.kt @@ -0,0 +1,12 @@ +package st.slex.csplashscreen.core.ui.mvi + +interface Store { + + interface State + + interface Event + + interface Navigation + + interface Action +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dc6fd32b..33eb1987 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,4 +1,5 @@ [versions] +kermit = "2.0.2" ktor = "2.3.6" logback = "1.4.11" compose = "1.5.4" @@ -19,6 +20,7 @@ kotlin = "1.9.20" junit = "4.13.2" [libraries] +kermit = { module = "co.touchlab:kermit", version.ref = "kermit" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" } junit = { group = "junit", name = "junit", version.ref = "junit" } diff --git a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/settings.gradle.kts b/settings.gradle.kts index 45dac6a9..fb96de02 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,4 +20,6 @@ dependencyResolutionManagement { include(":server") include(":shared") -include(":composeApp") \ No newline at end of file +include(":composeApp") +include(":core") +include(":home") From d63b0e83b68d6be9ccb8ddecd539d755ee95c9c8 Mon Sep 17 00:00:00 2001 From: stslex Date: Thu, 23 Nov 2023 22:11:12 +0300 Subject: [PATCH 2/3] inititate module structure --- composeApp/build.gradle.kts | 4 +- composeApp/src/commonMain/kotlin/App.kt | 2 +- .../src/commonMain/kotlin/InitialApp.kt | 58 +---------------- .../kotlin/main_screen/MainScreen.kt | 24 ------- .../resources/compose-multiplatform.xml | 36 ---------- core/core/build.gradle.kts | 46 +++++++++++++ .../com/stslex/core}/core/AppDispatcher.kt | 5 +- .../com/stslex/core}/core/CommonUtils.kt | 2 +- .../kotlin/com/stslex/core}/core/Logger.kt | 2 +- core/ui/build.gradle.kts | 52 +++++++++++++++ .../com/stslex/core/ui/mvi}/BaseStore.kt | 18 ++--- .../kotlin/com/stslex/core/ui/mvi}/Store.kt | 2 +- .../com/stslex/core/ui/navigation}/Router.kt | 4 +- .../com/stslex/core/ui/theme}/AppTheme.kt | 2 + feature/home/build.gradle.kts | 53 +++++++++++++++ .../com/stslex/feature/home/HomeScreen.kt | 65 +++++++++++++++++++ settings.gradle.kts | 5 +- 17 files changed, 244 insertions(+), 136 deletions(-) delete mode 100644 composeApp/src/commonMain/kotlin/main_screen/MainScreen.kt delete mode 100644 composeApp/src/commonMain/resources/compose-multiplatform.xml create mode 100644 core/core/build.gradle.kts rename {composeApp/src/commonMain/kotlin => core/core/src/commonMain/kotlin/com/stslex/core}/core/AppDispatcher.kt (81%) rename {composeApp/src/commonMain/kotlin => core/core/src/commonMain/kotlin/com/stslex/core}/core/CommonUtils.kt (84%) rename {composeApp/src/commonMain/kotlin => core/core/src/commonMain/kotlin/com/stslex/core}/core/Logger.kt (96%) create mode 100644 core/ui/build.gradle.kts rename {composeApp/src/commonMain/kotlin/store => core/ui/src/commonMain/kotlin/com/stslex/core/ui/mvi}/BaseStore.kt (86%) rename {composeApp/src/commonMain/kotlin/store => core/ui/src/commonMain/kotlin/com/stslex/core/ui/mvi}/Store.kt (72%) rename {composeApp/src/commonMain/kotlin/store => core/ui/src/commonMain/kotlin/com/stslex/core/ui/navigation}/Router.kt (52%) rename {composeApp/src/commonMain/kotlin => core/ui/src/commonMain/kotlin/com/stslex/core/ui/theme}/AppTheme.kt (93%) create mode 100644 feature/home/build.gradle.kts create mode 100644 feature/home/src/commonMain/kotlin/com/stslex/feature/home/HomeScreen.kt diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 3d6e766d..7e74ae51 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -41,7 +41,9 @@ kotlin { implementation(compose.desktop.currentOs) } commonMain.dependencies { - implementation(libs.kermit) + implementation(project(":core:core")) + implementation(project(":core:ui")) + implementation(project(":feature:home")) implementation(projects.shared) implementation(compose.runtime) diff --git a/composeApp/src/commonMain/kotlin/App.kt b/composeApp/src/commonMain/kotlin/App.kt index 73babd79..aa8a6b2b 100644 --- a/composeApp/src/commonMain/kotlin/App.kt +++ b/composeApp/src/commonMain/kotlin/App.kt @@ -1,5 +1,5 @@ import androidx.compose.runtime.Composable -import org.jetbrains.compose.resources.ExperimentalResourceApi +import com.stslex.core.ui.theme.AppTheme @Composable fun App() { diff --git a/composeApp/src/commonMain/kotlin/InitialApp.kt b/composeApp/src/commonMain/kotlin/InitialApp.kt index 6a076584..272a6384 100644 --- a/composeApp/src/commonMain/kotlin/InitialApp.kt +++ b/composeApp/src/commonMain/kotlin/InitialApp.kt @@ -1,61 +1,7 @@ -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.material.Button -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import org.jetbrains.compose.resources.ExperimentalResourceApi -import org.jetbrains.compose.resources.painterResource +import com.stslex.feature.home.HomeScreen -@OptIn(ExperimentalResourceApi::class) @Composable fun InitialApp() { - Box( - modifier = Modifier - .fillMaxSize() - .background(MaterialTheme.colors.background) - ) { - val defaultGreetState = "Hello World!" - val clickedGreetState = "Compose: ${Greeting().greet()}" - var isClicked by remember { mutableStateOf(false) } - val greetingText by remember { - derivedStateOf { - if (isClicked) { - clickedGreetState - } else { - defaultGreetState - } - } - } - Column( - modifier = Modifier.fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Button( - onClick = { - isClicked = !isClicked - } - ) { - Text(greetingText) - } - AnimatedVisibility(isClicked) { - Image( - painterResource("compose-multiplatform.xml"), - null - ) - } - } - } + HomeScreen() } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/main_screen/MainScreen.kt b/composeApp/src/commonMain/kotlin/main_screen/MainScreen.kt deleted file mode 100644 index e6443bfd..00000000 --- a/composeApp/src/commonMain/kotlin/main_screen/MainScreen.kt +++ /dev/null @@ -1,24 +0,0 @@ -package main_screen - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material.MaterialTheme -import androidx.compose.material.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier - -@Composable -fun MainScreen( - modifier: Modifier = Modifier -) { - Box( - modifier = modifier - .fillMaxSize() - .background(MaterialTheme.colors.background), - contentAlignment = Alignment.Center - ) { - Text(text = "Main Screen") - } -} \ No newline at end of file diff --git a/composeApp/src/commonMain/resources/compose-multiplatform.xml b/composeApp/src/commonMain/resources/compose-multiplatform.xml deleted file mode 100644 index d7bf7955..00000000 --- a/composeApp/src/commonMain/resources/compose-multiplatform.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - diff --git a/core/core/build.gradle.kts b/core/core/build.gradle.kts new file mode 100644 index 00000000..e3955751 --- /dev/null +++ b/core/core/build.gradle.kts @@ -0,0 +1,46 @@ +plugins { + alias(libs.plugins.kotlinMultiplatform) + alias(libs.plugins.androidLibrary) + alias(libs.plugins.jetbrainsCompose) +} + +kotlin { + androidTarget { + compilations.all { + kotlinOptions { + jvmTarget = "1.8" + } + } + } + + listOf( + iosX64(), + iosArm64(), + iosSimulatorArm64() + ).forEach { + it.binaries.framework { + baseName = "core" + isStatic = true + } + } + + sourceSets { + commonMain.dependencies { + implementation(libs.kermit) + implementation(projects.shared) + implementation(compose.runtime) + implementation(compose.foundation) + } + commonTest.dependencies { + implementation(libs.kotlin.test) + } + } +} + +android { + namespace = "com.stslex.core.core" + compileSdk = libs.versions.android.compileSdk.get().toInt() + defaultConfig { + minSdk = libs.versions.android.minSdk.get().toInt() + } +} diff --git a/composeApp/src/commonMain/kotlin/core/AppDispatcher.kt b/core/core/src/commonMain/kotlin/com/stslex/core/core/AppDispatcher.kt similarity index 81% rename from composeApp/src/commonMain/kotlin/core/AppDispatcher.kt rename to core/core/src/commonMain/kotlin/com/stslex/core/core/AppDispatcher.kt index 973b9ddd..b3230f93 100644 --- a/composeApp/src/commonMain/kotlin/core/AppDispatcher.kt +++ b/core/core/src/commonMain/kotlin/com/stslex/core/core/AppDispatcher.kt @@ -1,8 +1,7 @@ -package core +package com.stslex.core.core import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.IO import kotlinx.coroutines.MainCoroutineDispatcher interface AppDispatcher { @@ -12,7 +11,7 @@ interface AppDispatcher { } class AppDispatcherImpl : AppDispatcher { - override val io: CoroutineDispatcher = Dispatchers.IO + override val io: CoroutineDispatcher = Dispatchers.Default // TODO IO override val main: MainCoroutineDispatcher = Dispatchers.Main override val default: CoroutineDispatcher = Dispatchers.Default } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/core/CommonUtils.kt b/core/core/src/commonMain/kotlin/com/stslex/core/core/CommonUtils.kt similarity index 84% rename from composeApp/src/commonMain/kotlin/core/CommonUtils.kt rename to core/core/src/commonMain/kotlin/com/stslex/core/core/CommonUtils.kt index 056973a2..4ad5199e 100644 --- a/composeApp/src/commonMain/kotlin/core/CommonUtils.kt +++ b/core/core/src/commonMain/kotlin/com/stslex/core/core/CommonUtils.kt @@ -1,4 +1,4 @@ -package core +package com.stslex.core.core import kotlinx.coroutines.CoroutineExceptionHandler diff --git a/composeApp/src/commonMain/kotlin/core/Logger.kt b/core/core/src/commonMain/kotlin/com/stslex/core/core/Logger.kt similarity index 96% rename from composeApp/src/commonMain/kotlin/core/Logger.kt rename to core/core/src/commonMain/kotlin/com/stslex/core/core/Logger.kt index 5e04aca9..01b2c613 100644 --- a/composeApp/src/commonMain/kotlin/core/Logger.kt +++ b/core/core/src/commonMain/kotlin/com/stslex/core/core/Logger.kt @@ -1,4 +1,4 @@ -package core +package com.stslex.core.core import co.touchlab.kermit.Logger as Log diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts new file mode 100644 index 00000000..95919e1b --- /dev/null +++ b/core/ui/build.gradle.kts @@ -0,0 +1,52 @@ +import org.jetbrains.compose.ExperimentalComposeLibrary + +plugins { + alias(libs.plugins.kotlinMultiplatform) + alias(libs.plugins.androidLibrary) + alias(libs.plugins.jetbrainsCompose) +} + +kotlin { + androidTarget { + compilations.all { + kotlinOptions { + jvmTarget = "1.8" + } + } + } + + listOf( + iosX64(), + iosArm64(), + iosSimulatorArm64() + ).forEach { + it.binaries.framework { + baseName = "ui" + isStatic = true + } + } + + sourceSets { + commonMain.dependencies { + implementation(project(":core:core")) + + implementation(projects.shared) + implementation(compose.runtime) + implementation(compose.foundation) + implementation(compose.material) + @OptIn(ExperimentalComposeLibrary::class) + implementation(compose.components.resources) + } + commonTest.dependencies { + implementation(libs.kotlin.test) + } + } +} + +android { + namespace = "com.stslex.core.ui" + compileSdk = libs.versions.android.compileSdk.get().toInt() + defaultConfig { + minSdk = libs.versions.android.minSdk.get().toInt() + } +} diff --git a/composeApp/src/commonMain/kotlin/store/BaseStore.kt b/core/ui/src/commonMain/kotlin/com/stslex/core/ui/mvi/BaseStore.kt similarity index 86% rename from composeApp/src/commonMain/kotlin/store/BaseStore.kt rename to core/ui/src/commonMain/kotlin/com/stslex/core/ui/mvi/BaseStore.kt index c1892728..41e525e8 100644 --- a/composeApp/src/commonMain/kotlin/store/BaseStore.kt +++ b/core/ui/src/commonMain/kotlin/com/stslex/core/ui/mvi/BaseStore.kt @@ -1,8 +1,13 @@ -package store +package com.stslex.core.ui.mvi -import core.AppDispatcher -import core.Logger -import core.coroutineExceptionHandler +import com.stslex.core.core.AppDispatcher +import com.stslex.core.core.Logger +import com.stslex.core.core.coroutineExceptionHandler +import com.stslex.core.ui.navigation.Router +import com.stslex.core.ui.mvi.Store.Action +import com.stslex.core.ui.mvi.Store.Event +import com.stslex.core.ui.mvi.Store.Navigation +import com.stslex.core.ui.mvi.Store.State import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job @@ -16,11 +21,6 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import st.slex.csplashscreen.core.ui.mvi.Router -import st.slex.csplashscreen.core.ui.mvi.Store.Action -import st.slex.csplashscreen.core.ui.mvi.Store.Event -import st.slex.csplashscreen.core.ui.mvi.Store.Navigation -import st.slex.csplashscreen.core.ui.mvi.Store.State abstract class BaseStore( private val router: Router, diff --git a/composeApp/src/commonMain/kotlin/store/Store.kt b/core/ui/src/commonMain/kotlin/com/stslex/core/ui/mvi/Store.kt similarity index 72% rename from composeApp/src/commonMain/kotlin/store/Store.kt rename to core/ui/src/commonMain/kotlin/com/stslex/core/ui/mvi/Store.kt index a3117d6b..44fc4319 100644 --- a/composeApp/src/commonMain/kotlin/store/Store.kt +++ b/core/ui/src/commonMain/kotlin/com/stslex/core/ui/mvi/Store.kt @@ -1,4 +1,4 @@ -package st.slex.csplashscreen.core.ui.mvi +package com.stslex.core.ui.mvi interface Store { diff --git a/composeApp/src/commonMain/kotlin/store/Router.kt b/core/ui/src/commonMain/kotlin/com/stslex/core/ui/navigation/Router.kt similarity index 52% rename from composeApp/src/commonMain/kotlin/store/Router.kt rename to core/ui/src/commonMain/kotlin/com/stslex/core/ui/navigation/Router.kt index 4763faa2..bd22011c 100644 --- a/composeApp/src/commonMain/kotlin/store/Router.kt +++ b/core/ui/src/commonMain/kotlin/com/stslex/core/ui/navigation/Router.kt @@ -1,4 +1,6 @@ -package st.slex.csplashscreen.core.ui.mvi +package com.stslex.core.ui.navigation + +import com.stslex.core.ui.mvi.Store fun interface Router { operator fun invoke(event: E) diff --git a/composeApp/src/commonMain/kotlin/AppTheme.kt b/core/ui/src/commonMain/kotlin/com/stslex/core/ui/theme/AppTheme.kt similarity index 93% rename from composeApp/src/commonMain/kotlin/AppTheme.kt rename to core/ui/src/commonMain/kotlin/com/stslex/core/ui/theme/AppTheme.kt index 89e9d566..b316513b 100644 --- a/composeApp/src/commonMain/kotlin/AppTheme.kt +++ b/core/ui/src/commonMain/kotlin/com/stslex/core/ui/theme/AppTheme.kt @@ -1,3 +1,5 @@ +package com.stslex.core.ui.theme + import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material.MaterialTheme import androidx.compose.material.darkColors diff --git a/feature/home/build.gradle.kts b/feature/home/build.gradle.kts new file mode 100644 index 00000000..3098c728 --- /dev/null +++ b/feature/home/build.gradle.kts @@ -0,0 +1,53 @@ +import org.jetbrains.compose.ExperimentalComposeLibrary + +plugins { + alias(libs.plugins.kotlinMultiplatform) + alias(libs.plugins.androidLibrary) + alias(libs.plugins.jetbrainsCompose) +} + +kotlin { + androidTarget { + compilations.all { + kotlinOptions { + jvmTarget = "1.8" + } + } + } + + listOf( + iosX64(), + iosArm64(), + iosSimulatorArm64() + ).forEach { + it.binaries.framework { + baseName = "home" + isStatic = true + } + } + + sourceSets { + commonMain.dependencies { + implementation(project(":core:core")) + implementation(project(":core:ui")) + + implementation(projects.shared) + implementation(compose.runtime) + implementation(compose.foundation) + implementation(compose.material) + @OptIn(ExperimentalComposeLibrary::class) + implementation(compose.components.resources) + } + commonTest.dependencies { + implementation(libs.kotlin.test) + } + } +} + +android { + namespace = "com.stslex.feature.home" + compileSdk = libs.versions.android.compileSdk.get().toInt() + defaultConfig { + minSdk = libs.versions.android.minSdk.get().toInt() + } +} diff --git a/feature/home/src/commonMain/kotlin/com/stslex/feature/home/HomeScreen.kt b/feature/home/src/commonMain/kotlin/com/stslex/feature/home/HomeScreen.kt new file mode 100644 index 00000000..6ae61bad --- /dev/null +++ b/feature/home/src/commonMain/kotlin/com/stslex/feature/home/HomeScreen.kt @@ -0,0 +1,65 @@ +package com.stslex.feature.home + +import Greeting +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material.Button +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Home +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier + +@Composable +fun HomeScreen( + modifier: Modifier = Modifier +) { + Box( + modifier = modifier + .fillMaxSize() + .background(MaterialTheme.colors.background) + ) { + val defaultGreetState = "Hello World!" + val clickedGreetState = "Compose: ${Greeting().greet()}" + var isClicked by remember { mutableStateOf(false) } + val greetingText by remember { + derivedStateOf { + if (isClicked) { + clickedGreetState + } else { + defaultGreetState + } + } + } + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Button( + onClick = { + isClicked = !isClicked + } + ) { + Text(greetingText) + } + AnimatedVisibility(isClicked) { + Image( + Icons.Default.Home, + null + ) + } + } + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index fb96de02..239c3fa5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,5 +21,6 @@ dependencyResolutionManagement { include(":server") include(":shared") include(":composeApp") -include(":core") -include(":home") +include(":core:core") +include(":core:ui") +include(":feature:home") From 28f7eacde5b426307c3d55d6dd29777a07a163bf Mon Sep 17 00:00:00 2001 From: stslex Date: Thu, 23 Nov 2023 22:41:02 +0300 Subject: [PATCH 3/3] fix pipeline, add ci --- .fleet/receipt.json | 29 -------------- .github/workflows/build_app.yml | 38 +++++++++++++++++++ .gitignore | 1 + core/core/build.gradle.kts | 7 ++++ core/ui/build.gradle.kts | 7 ++++ feature/home/build.gradle.kts | 10 +++++ .../com/stslex/feature/home/HomeScreen.kt | 7 ++-- .../resources/compose-multiplatform.xml | 36 ++++++++++++++++++ 8 files changed, 103 insertions(+), 32 deletions(-) delete mode 100644 .fleet/receipt.json create mode 100644 .github/workflows/build_app.yml create mode 100644 feature/home/src/commonMain/resources/compose-multiplatform.xml diff --git a/.fleet/receipt.json b/.fleet/receipt.json deleted file mode 100644 index 97e9bfdd..00000000 --- a/.fleet/receipt.json +++ /dev/null @@ -1,29 +0,0 @@ -// Project generated by Kotlin Multiplatform Wizard -{ - "spec": { - "template_id": "kmt", - "targets": { - "android": { - "ui": [ - "compose" - ] - }, - "ios": { - "ui": [ - "compose" - ] - }, - "desktop": { - "ui": [ - "compose" - ] - }, - "server": { - "engine": [ - "ktor" - ] - } - } - }, - "timestamp": "2023-11-07T19:37:30.261131495Z" -} \ No newline at end of file diff --git a/.github/workflows/build_app.yml b/.github/workflows/build_app.yml new file mode 100644 index 00000000..12a6f705 --- /dev/null +++ b/.github/workflows/build_app.yml @@ -0,0 +1,38 @@ +name: Project Build + +on: + push: + branches: [ main ] + pull_request: + workflow_dispatch: + +jobs: + build: + + runs-on: macos-latest + timeout-minutes: 60 + + steps: + + - name: Checkout branch + uses: actions/checkout@v2 + + - name: set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + cache: gradle + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + + - name: Build with Gradle + run: ./gradlew build + + - name: Junit tests with Gradle + run: ./gradlew testDebugUnitTest \ No newline at end of file diff --git a/.gitignore b/.gitignore index 33e4c75d..8cba68c5 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ captures !*.xcodeproj/project.xcworkspace/ !*.xcworkspace/contents.xcworkspacedata **/xcshareddata/WorkspaceSettings.xcsettings +.fleet diff --git a/core/core/build.gradle.kts b/core/core/build.gradle.kts index e3955751..aef9daf8 100644 --- a/core/core/build.gradle.kts +++ b/core/core/build.gradle.kts @@ -12,6 +12,8 @@ kotlin { } } } + + jvm("desktop") listOf( iosX64(), @@ -25,6 +27,8 @@ kotlin { } sourceSets { + val desktopMain by getting + commonMain.dependencies { implementation(libs.kermit) implementation(projects.shared) @@ -34,6 +38,9 @@ kotlin { commonTest.dependencies { implementation(libs.kotlin.test) } + desktopMain.dependencies { + implementation(compose.desktop.currentOs) + } } } diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index 95919e1b..184ee588 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -15,6 +15,8 @@ kotlin { } } + jvm("desktop") + listOf( iosX64(), iosArm64(), @@ -27,6 +29,8 @@ kotlin { } sourceSets { + val desktopMain by getting + commonMain.dependencies { implementation(project(":core:core")) @@ -40,6 +44,9 @@ kotlin { commonTest.dependencies { implementation(libs.kotlin.test) } + desktopMain.dependencies { + implementation(compose.desktop.currentOs) + } } } diff --git a/feature/home/build.gradle.kts b/feature/home/build.gradle.kts index 3098c728..f2be90b6 100644 --- a/feature/home/build.gradle.kts +++ b/feature/home/build.gradle.kts @@ -15,6 +15,8 @@ kotlin { } } + jvm("desktop") + listOf( iosX64(), iosArm64(), @@ -27,6 +29,8 @@ kotlin { } sourceSets { + val desktopMain by getting + commonMain.dependencies { implementation(project(":core:core")) implementation(project(":core:ui")) @@ -41,6 +45,9 @@ kotlin { commonTest.dependencies { implementation(libs.kotlin.test) } + desktopMain.dependencies { + implementation(compose.desktop.currentOs) + } } } @@ -50,4 +57,7 @@ android { defaultConfig { minSdk = libs.versions.android.minSdk.get().toInt() } + sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") + sourceSets["main"].res.srcDirs("src/androidMain/res") + sourceSets["main"].resources.srcDirs("src/commonMain/resources") } diff --git a/feature/home/src/commonMain/kotlin/com/stslex/feature/home/HomeScreen.kt b/feature/home/src/commonMain/kotlin/com/stslex/feature/home/HomeScreen.kt index 6ae61bad..c38fc7b6 100644 --- a/feature/home/src/commonMain/kotlin/com/stslex/feature/home/HomeScreen.kt +++ b/feature/home/src/commonMain/kotlin/com/stslex/feature/home/HomeScreen.kt @@ -11,8 +11,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.material.Button import androidx.compose.material.MaterialTheme import androidx.compose.material.Text -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Home import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue @@ -21,7 +19,10 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import org.jetbrains.compose.resources.ExperimentalResourceApi +import org.jetbrains.compose.resources.painterResource +@OptIn(ExperimentalResourceApi::class) @Composable fun HomeScreen( modifier: Modifier = Modifier @@ -56,7 +57,7 @@ fun HomeScreen( } AnimatedVisibility(isClicked) { Image( - Icons.Default.Home, + painterResource("compose-multiplatform.xml"), null ) } diff --git a/feature/home/src/commonMain/resources/compose-multiplatform.xml b/feature/home/src/commonMain/resources/compose-multiplatform.xml new file mode 100644 index 00000000..d7bf7955 --- /dev/null +++ b/feature/home/src/commonMain/resources/compose-multiplatform.xml @@ -0,0 +1,36 @@ + + + + + + + +