From b83bec3a5495d9cf0f6b03b080f7537bfb9caa36 Mon Sep 17 00:00:00 2001 From: Danil Nikolaev Date: Mon, 5 Aug 2024 06:42:41 +0300 Subject: [PATCH] added wasm/js target --- README.MD | 3 ++ composeApp/build.gradle.kts | 34 +++++++++++++++++-- .../model/SettingsStoreProvider.android.kt | 13 +++++++ .../model/SettingsStoreProvider.apple.kt | 11 ++++++ .../overseerr/network/di/NetworkModule.kt | 3 +- .../overseerr/settings/SettingsController.kt | 9 ++--- .../overseerr/settings/di/SettingsModule.kt | 9 ++--- .../settings/model/SettingsStoreProvider.kt | 8 +++++ .../model/SettingsStoreProvider.jvm.kt | 12 +++++++ .../meloda/overseerr/model/Platform.wasmJs.kt | 5 +++ .../model/SettingsStoreProvider.wasmJs.kt | 8 +++++ .../meloda/overseerr/theme/Theme.wasmJs.kt | 7 ++++ composeApp/src/wasmJsMain/kotlin/main.kt | 16 +++++++++ .../src/wasmJsMain/resources/index.html | 12 +++++++ .../src/wasmJsMain/resources/styles.css | 7 ++++ gradle/libs.versions.toml | 1 + settings.gradle.kts | 1 - 17 files changed, 141 insertions(+), 18 deletions(-) create mode 100644 composeApp/src/androidMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.android.kt create mode 100644 composeApp/src/appleMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.apple.kt create mode 100644 composeApp/src/commonMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.kt create mode 100644 composeApp/src/jvmMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.jvm.kt create mode 100644 composeApp/src/wasmJsMain/kotlin/dev/meloda/overseerr/model/Platform.wasmJs.kt create mode 100644 composeApp/src/wasmJsMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.wasmJs.kt create mode 100644 composeApp/src/wasmJsMain/kotlin/dev/meloda/overseerr/theme/Theme.wasmJs.kt create mode 100644 composeApp/src/wasmJsMain/kotlin/main.kt create mode 100644 composeApp/src/wasmJsMain/resources/index.html create mode 100644 composeApp/src/wasmJsMain/resources/styles.css diff --git a/README.MD b/README.MD index ad176c9..781b10c 100644 --- a/README.MD +++ b/README.MD @@ -24,3 +24,6 @@ To run the application on iPhone device/simulator: - Or use [Kotlin Multiplatform Mobile plugin](https://plugins.jetbrains.com/plugin/14936-kotlin-multiplatform-mobile) for Android Studio Run iOS simulator UI tests: `./gradlew :composeApp:iosSimulatorArm64Test` +### Wasm Browser (Alpha) +Run the browser application: `./gradlew :composeApp:wasmJsBrowserDevelopmentRun --continue` +Run browser UI tests: `./gradlew :composeApp:wasmJsBrowserTest` diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 90c7640..eb3f740 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -2,8 +2,10 @@ import com.android.build.api.dsl.ManagedVirtualDevice import org.jetbrains.compose.ExperimentalComposeLibrary import org.jetbrains.compose.desktop.application.dsl.TargetFormat import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetTree +import org.jetbrains.kotlin.gradle.targets.js.webpack.KotlinWebpackConfig plugins { alias(libs.plugins.multiplatform) @@ -14,6 +16,24 @@ plugins { } kotlin { + @OptIn(ExperimentalWasmDsl::class) + wasmJs { + moduleName = "composeApp" + browser { + val projectDirPath = project.projectDir.path + commonWebpackConfig { + outputFileName = "composeApp.js" + devServer = (devServer ?: KotlinWebpackConfig.DevServer()).apply { + static = (static ?: mutableListOf()).apply { + // Serve sources to debug inside browser + add(projectDirPath) + } + } + } + } + binaries.executable() + } + // export correct artifact to use all classes of library directly from Swift targets.withType(org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget::class.java).all { binaries.withType(org.jetbrains.kotlin.gradle.plugin.mpp.Framework::class.java).all { @@ -64,7 +84,6 @@ kotlin { implementation(libs.coil.network.ktor) implementation(libs.kotlinx.coroutines.core) implementation(libs.ktor.core) - implementation(libs.ktor.client.cio) implementation(libs.kotlinx.serialization.json) implementation(libs.koin.core) implementation(libs.koin.compose) @@ -72,7 +91,6 @@ kotlin { implementation(libs.haze) implementation(libs.haze.materials) implementation(libs.kstore) - implementation(libs.kstore.file) implementation(libs.napier) } @@ -88,6 +106,7 @@ kotlin { implementation(libs.androidx.activityCompose) implementation(libs.kotlinx.coroutines.android) implementation(libs.ktor.client.okhttp) + implementation(libs.kstore.file) } jvmMain.dependencies { @@ -95,10 +114,16 @@ kotlin { implementation(libs.kotlinx.coroutines.swing) implementation(libs.ktor.client.okhttp) implementation(libs.appdirs) + implementation(libs.kstore.file) } iosMain.dependencies { implementation(libs.ktor.client.darwin) + implementation(libs.kstore.file) + } + + wasmJsMain.dependencies { + implementation(libs.kstore.storage) } } } @@ -128,6 +153,11 @@ android { } } } + buildTypes { + getByName("release") { + isMinifyEnabled = false + } + } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 diff --git a/composeApp/src/androidMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.android.kt b/composeApp/src/androidMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.android.kt new file mode 100644 index 0000000..e3f3756 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.android.kt @@ -0,0 +1,13 @@ +package dev.meloda.overseerr.settings.model + +import dev.meloda.overseerr.appDir +import io.github.xxfast.kstore.KStore +import io.github.xxfast.kstore.file.storeOf +import okio.Path.Companion.toPath + +actual class SettingsStoreProvider actual constructor() { + + actual fun provideStore(): KStore { + return storeOf(file = "$appDir/app_settings.json".toPath()) + } +} diff --git a/composeApp/src/appleMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.apple.kt b/composeApp/src/appleMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.apple.kt new file mode 100644 index 0000000..9251300 --- /dev/null +++ b/composeApp/src/appleMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.apple.kt @@ -0,0 +1,11 @@ +package dev.meloda.overseerr.settings.model + +import dev.meloda.overseerr.appDir +import io.github.xxfast.kstore.KStore +import okio.Path.Companion.toPath + +actual class SettingsStoreProvider actual constructor() { + actual fun provideStore(): KStore { + return storeOf(file = "$appDir/app_settings.json".toPath()) + } +} diff --git a/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/network/di/NetworkModule.kt b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/network/di/NetworkModule.kt index 5ada39c..cf6db0b 100644 --- a/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/network/di/NetworkModule.kt +++ b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/network/di/NetworkModule.kt @@ -1,12 +1,11 @@ package dev.meloda.overseerr.network.di import io.ktor.client.* -import io.ktor.client.engine.cio.* import org.koin.dsl.module val networkModule = module { single { - HttpClient(CIO) { + HttpClient { } } diff --git a/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/settings/SettingsController.kt b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/settings/SettingsController.kt index cdc8436..cc9a8a1 100644 --- a/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/settings/SettingsController.kt +++ b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/settings/SettingsController.kt @@ -3,11 +3,8 @@ package dev.meloda.overseerr.settings import dev.meloda.overseerr.ext.setValue import dev.meloda.overseerr.settings.model.AppSettings import io.github.xxfast.kstore.KStore -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.IO import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.withContext interface SettingsController { val settings: StateFlow @@ -22,13 +19,13 @@ class SettingsControllerImpl( override val settings = MutableStateFlow(AppSettings.EMPTY) - override suspend fun updateAppSettings(update: (AppSettings) -> AppSettings) = withContext(Dispatchers.IO) { + override suspend fun updateAppSettings(update: (AppSettings) -> AppSettings) { store.set(update(settings.value)) } - override suspend fun loadAppSettings(): AppSettings = withContext(Dispatchers.IO) { + override suspend fun loadAppSettings(): AppSettings { val loadedSettings = store.get() ?: AppSettings.EMPTY settings.setValue { loadedSettings } - loadedSettings + return loadedSettings } } diff --git a/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/settings/di/SettingsModule.kt b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/settings/di/SettingsModule.kt index 5e04ea2..3e71e38 100644 --- a/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/settings/di/SettingsModule.kt +++ b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/settings/di/SettingsModule.kt @@ -1,18 +1,13 @@ package dev.meloda.overseerr.settings.di -import dev.meloda.overseerr.appDir import dev.meloda.overseerr.settings.SettingsController import dev.meloda.overseerr.settings.SettingsControllerImpl -import dev.meloda.overseerr.settings.model.AppSettings -import io.github.xxfast.kstore.file.storeOf -import okio.Path.Companion.toPath +import dev.meloda.overseerr.settings.model.SettingsStoreProvider import org.koin.core.module.dsl.singleOf import org.koin.dsl.bind import org.koin.dsl.module val settingsModule = module { - single { - storeOf(file = "$appDir/app_settings.json".toPath()) - } + single { SettingsStoreProvider().provideStore() } singleOf(::SettingsControllerImpl) bind SettingsController::class } diff --git a/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.kt b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.kt new file mode 100644 index 0000000..15c690f --- /dev/null +++ b/composeApp/src/commonMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.kt @@ -0,0 +1,8 @@ +package dev.meloda.overseerr.settings.model + +import io.github.xxfast.kstore.KStore + +expect class SettingsStoreProvider() { + + fun provideStore(): KStore +} diff --git a/composeApp/src/jvmMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.jvm.kt b/composeApp/src/jvmMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.jvm.kt new file mode 100644 index 0000000..2da8c76 --- /dev/null +++ b/composeApp/src/jvmMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.jvm.kt @@ -0,0 +1,12 @@ +package dev.meloda.overseerr.settings.model + +import dev.meloda.overseerr.appDir +import io.github.xxfast.kstore.KStore +import io.github.xxfast.kstore.file.storeOf +import okio.Path.Companion.toPath + +actual class SettingsStoreProvider actual constructor() { + actual fun provideStore(): KStore { + return storeOf(file = "$appDir/app_settings.json".toPath()) + } +} diff --git a/composeApp/src/wasmJsMain/kotlin/dev/meloda/overseerr/model/Platform.wasmJs.kt b/composeApp/src/wasmJsMain/kotlin/dev/meloda/overseerr/model/Platform.wasmJs.kt new file mode 100644 index 0000000..d3c0331 --- /dev/null +++ b/composeApp/src/wasmJsMain/kotlin/dev/meloda/overseerr/model/Platform.wasmJs.kt @@ -0,0 +1,5 @@ +package dev.meloda.overseerr.model + +actual class Platform actual constructor() { + actual val name: String = "JS" +} diff --git a/composeApp/src/wasmJsMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.wasmJs.kt b/composeApp/src/wasmJsMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.wasmJs.kt new file mode 100644 index 0000000..40f720b --- /dev/null +++ b/composeApp/src/wasmJsMain/kotlin/dev/meloda/overseerr/settings/model/SettingsStoreProvider.wasmJs.kt @@ -0,0 +1,8 @@ +package dev.meloda.overseerr.settings.model + +import io.github.xxfast.kstore.KStore +import io.github.xxfast.kstore.storage.storeOf + +actual class SettingsStoreProvider actual constructor() { + actual fun provideStore(): KStore = storeOf(key = "app_settings") +} diff --git a/composeApp/src/wasmJsMain/kotlin/dev/meloda/overseerr/theme/Theme.wasmJs.kt b/composeApp/src/wasmJsMain/kotlin/dev/meloda/overseerr/theme/Theme.wasmJs.kt new file mode 100644 index 0000000..d8e4d65 --- /dev/null +++ b/composeApp/src/wasmJsMain/kotlin/dev/meloda/overseerr/theme/Theme.wasmJs.kt @@ -0,0 +1,7 @@ +package dev.meloda.overseerr.theme + +import androidx.compose.runtime.Composable + +@Composable +internal actual fun SystemAppearance(isDark: Boolean) { +} diff --git a/composeApp/src/wasmJsMain/kotlin/main.kt b/composeApp/src/wasmJsMain/kotlin/main.kt new file mode 100644 index 0000000..44878ab --- /dev/null +++ b/composeApp/src/wasmJsMain/kotlin/main.kt @@ -0,0 +1,16 @@ +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.window.ComposeViewport +import dev.meloda.overseerr.App +import dev.meloda.overseerr.di.appModule +import kotlinx.browser.document +import org.koin.core.context.startKoin + +@OptIn(ExperimentalComposeUiApi::class) +fun main() { + ComposeViewport(document.body!!) { + startKoin { + modules(appModule) + } + App() + } +} diff --git a/composeApp/src/wasmJsMain/resources/index.html b/composeApp/src/wasmJsMain/resources/index.html new file mode 100644 index 0000000..0be283f --- /dev/null +++ b/composeApp/src/wasmJsMain/resources/index.html @@ -0,0 +1,12 @@ + + + + + + KotlinProject + + + + + + \ No newline at end of file diff --git a/composeApp/src/wasmJsMain/resources/styles.css b/composeApp/src/wasmJsMain/resources/styles.css new file mode 100644 index 0000000..0549b10 --- /dev/null +++ b/composeApp/src/wasmJsMain/resources/styles.css @@ -0,0 +1,7 @@ +html, body { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow: hidden; +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d823a68..4e88ea2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -42,6 +42,7 @@ haze = { module = "dev.chrisbanes.haze:haze", version.ref = "haze" } haze-materials = { module = "dev.chrisbanes.haze:haze-materials", version.ref = "haze" } kstore = { module = "io.github.xxfast:kstore", version.ref = "kstore" } kstore-file = { module = "io.github.xxfast:kstore-file", version.ref = "kstore" } +kstore-storage = { module = "io.github.xxfast:kstore-storage", version.ref = "kstore" } appdirs = { module = "net.harawata:appdirs", version.ref = "appdirs" } napier = { module = "io.github.aakira:napier", version.ref = "napier" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 8e05f06..80c15fa 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -29,7 +29,6 @@ dependencyResolutionManagement { } } mavenCentral() - maven("https://maven.pkg.jetbrains.space/kotlin/p/wasm/experimental") maven("https://maven.pkg.jetbrains.space/public/p/ktor/eap") } }