From 0ba881e14e243befc97147aff68e19b472283385 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sat, 4 Jan 2025 23:23:15 +0800 Subject: [PATCH 01/16] Review "NavHost.js.kt" comparing it to the original "NavHost.kt", and try adding back some code related to `viewModelStoreOwner` --- .../androidx/navigation/compose/NavHost.js.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt b/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt index 3ef23d1a..1f334f8d 100644 --- a/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt +++ b/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt @@ -1,6 +1,8 @@ package com.huanshankeji.androidx.navigation.compose import androidx.compose.runtime.* +import androidx.lifecycle.ViewModelStore +import androidx.lifecycle.ViewModelStoreOwner import androidx.navigation.* import com.huanshankeji.compose.foundation.layout.Box import com.huanshankeji.compose.foundation.layout.fillMaxSize @@ -10,10 +12,11 @@ import com.huanshankeji.compose.ui.Modifier // copied and adapted from "NavHost.kt" in `androidx.navigation.compose` -/* -private class ComposeViewModelStoreOwner: ViewModelStoreOwner { +private class ComposeViewModelStoreOwner : ViewModelStoreOwner { override val viewModelStore: ViewModelStore = ViewModelStore() - fun dispose() { viewModelStore.clear() } + fun dispose() { + viewModelStore.clear() + } } @Composable @@ -24,9 +27,6 @@ private fun rememberViewModelStoreOwner(): ViewModelStoreOwner { } return viewModelStoreOwner } -*/ - - @Composable actual fun NavHost( @@ -56,9 +56,9 @@ actual fun NavHost( ) { //val lifecycleOwner = LocalLifecycleOwner.current - //val viewModelStoreOwner = LocalViewModelStoreOwner.current ?: rememberViewModelStoreOwner() + val viewModelStoreOwner = /*LocalViewModelStoreOwner.current ?:*/ rememberViewModelStoreOwner() - //navController.setViewModelStore(viewModelStoreOwner.viewModelStore) + navController.setViewModelStore(viewModelStoreOwner.viewModelStore) // Then set the graph navController.graph = graph @@ -110,7 +110,7 @@ actual fun NavHost( // ViewModelStoreOwner and LifecycleOwner currentEntry?.LocalOwnersProvider(saveableStateHolder) { (currentEntry.destination as ComposeNavigator.Destination) - .content( currentEntry) + .content(currentEntry) } */ currentEntry?.let { From ebfa280ed3f23196fdc2fadba78f1d1a379be05c Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Sun, 5 Jan 2025 10:10:54 +0800 Subject: [PATCH 02/16] Update the project version to be branch-specific --- buildSrc/src/main/kotlin/VersionsAndDependencies.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index 7720bc25..d3927b3a 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -1,7 +1,7 @@ import com.huanshankeji.CommonDependencies import org.jetbrains.compose.ComposeBuildConfig -val projectVersion = "0.5.1-SNAPSHOT" +val projectVersion = "0.5.1-save-viewmodel-in-navigation-on-js-dom-SNAPSHOT" val commonDependencies = CommonDependencies() From c547df59bb9a1de7e2b2fcd72df1d87ec23094f9 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Tue, 7 Jan 2025 09:31:32 +0800 Subject: [PATCH 03/16] Initially copy and adapt more ViewModel code from Compose Multiplatform The added code in the "common" module is temporarily commented out to see whether it's necessary. Under such a condition, an exception is thrown with the message "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner" on JS DOM in an internal app but not in the demo. --- common/build.gradle.kts | 8 +++ .../ui/platform/DefaultViewModelOwnerStore.kt | 25 +++++++ .../compose/ui/window/ComposeWindow.js.kt | 28 ++++++++ .../src/commonMain/kotlin/ViewModel.kt | 43 ++++++++++++ .../kotlin/ViewModel.composeUi.kt | 30 +++++++- .../jsMain/kotlin/LocalViewModelStoreOwner.kt | 26 +++++++ .../src/jsMain/kotlin/ViewModel.js.kt | 68 ++++++++++++++++++- 7 files changed, 223 insertions(+), 5 deletions(-) create mode 100644 common/src/jsMain/kotlin/com/huanshankeji/compose/ui/platform/DefaultViewModelOwnerStore.kt create mode 100644 common/src/jsMain/kotlin/com/huanshankeji/compose/ui/window/ComposeWindow.js.kt create mode 100644 lifecycle-viewmodel/src/jsMain/kotlin/LocalViewModelStoreOwner.kt diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 1b37d89f..56cd8b8c 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -56,6 +56,14 @@ kotlin { // see: https://github.com/varabyte/kobweb/blob/main/frontend/kobweb-compose/build.gradle.kts api("com.varabyte.kobweb:kobweb-compose:${DependencyVersions.kobweb}") implementation("com.huanshankeji:compose-html-common:${DependencyVersions.huanshankejiComposeHtml}") + + // TODO not used yet + /* + The UI module depends on the lifecycle module to use `androidx.lifecycle.ViewModelStoreOwner`. + See https://github.com/JetBrains/compose-multiplatform-core/blob/jb-main/compose/ui/ui/build.gradle#L87. + This is actually only needed for JS DOM. + */ + //implementation(cpnProject(project, ":lifecycle-viewmodel")) } } } diff --git a/common/src/jsMain/kotlin/com/huanshankeji/compose/ui/platform/DefaultViewModelOwnerStore.kt b/common/src/jsMain/kotlin/com/huanshankeji/compose/ui/platform/DefaultViewModelOwnerStore.kt new file mode 100644 index 00000000..1bdcdee8 --- /dev/null +++ b/common/src/jsMain/kotlin/com/huanshankeji/compose/ui/platform/DefaultViewModelOwnerStore.kt @@ -0,0 +1,25 @@ +package com.huanshankeji.compose.ui.platform + +/* +// copied and adapted from "DefaultViewModelOwnerStore.skiko.kt" in `androidx.compose.ui.platform` + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.InternalComposeApi +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.lifecycle.ViewModelStoreOwner + +/** + * Internal helper to provide [ViewModelStoreOwner] from Compose UI module. + * In applications please use [androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner]. + * + * @hide + */ +internal val LocalInternalViewModelStoreOwner = staticCompositionLocalOf { + null +} + +@InternalComposeApi +@Composable +fun findComposeDefaultViewModelStoreOwner(): ViewModelStoreOwner? = + LocalInternalViewModelStoreOwner.current +*/ diff --git a/common/src/jsMain/kotlin/com/huanshankeji/compose/ui/window/ComposeWindow.js.kt b/common/src/jsMain/kotlin/com/huanshankeji/compose/ui/window/ComposeWindow.js.kt new file mode 100644 index 00000000..3c8e443c --- /dev/null +++ b/common/src/jsMain/kotlin/com/huanshankeji/compose/ui/window/ComposeWindow.js.kt @@ -0,0 +1,28 @@ +package com.huanshankeji.compose.ui.window + +/* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Composition +import androidx.compose.runtime.CompositionLocalProvider +import com.huanshankeji.compose.ui.platform.LocalInternalViewModelStoreOwner +import org.jetbrains.compose.web.dom.DOMScope +import org.jetbrains.compose.web.renderComposableInBody +import org.w3c.dom.HTMLBodyElement + +// TODO not used yet so made private +private fun renderComposableInBodyWithLifecycle( + content: @Composable DOMScope.() -> Unit +): Composition = + renderComposableInBody { + // copied and adapted from `ComposeWindow` in "ComposeWindow.web.kt" in `androidx.compose.ui.window` + // also see `ComposeViewport` on Wasm JS + CompositionLocalProvider( + //LocalSystemTheme provides systemThemeObserver.currentSystemTheme.value, // TODO add back if needed one day + //LocalLifecycleOwner provides this, // TODO + LocalInternalViewModelStoreOwner provides TODO(), + content = { + content() + } + ) + } +*/ diff --git a/lifecycle-viewmodel/src/commonMain/kotlin/ViewModel.kt b/lifecycle-viewmodel/src/commonMain/kotlin/ViewModel.kt index 1a6ea197..3057449f 100644 --- a/lifecycle-viewmodel/src/commonMain/kotlin/ViewModel.kt +++ b/lifecycle-viewmodel/src/commonMain/kotlin/ViewModel.kt @@ -1,13 +1,56 @@ package com.huanshankeji.androidx.lifecycle.viewmodel.compose import androidx.compose.runtime.Composable +import androidx.lifecycle.HasDefaultViewModelProviderFactory import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelStoreOwner import androidx.lifecycle.viewmodel.CreationExtras +import kotlin.reflect.KClass // https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-viewmodel.html +// copied and adapted from "ViewModel.kt" in `androidx.lifecycle.viewmodel.compose` + + +// `expect` can be removed if `expect object LocalViewModelStoreOwner` is added. +@PublishedApi +@Composable +internal expect fun defaultViewModelStoreOwner(): ViewModelStoreOwner + +@PublishedApi +internal fun ViewModelStoreOwner.defaultCreationExtras(): CreationExtras = + if (this is HasDefaultViewModelProviderFactory) { + this.defaultViewModelCreationExtras + } else { + CreationExtras.Empty + } + + +@Composable +expect fun viewModel( + modelClass: KClass, + viewModelStoreOwner: ViewModelStoreOwner = defaultViewModelStoreOwner(), + key: String? = null, + factory: ViewModelProvider.Factory? = null, + extras: CreationExtras = viewModelStoreOwner.defaultCreationExtras() +): VM + @Composable expect inline fun viewModel( + viewModelStoreOwner: ViewModelStoreOwner = defaultViewModelStoreOwner(), key: String? = null, noinline initializer: CreationExtras.() -> VM ): VM + +@Deprecated( + "Use the one with a `viewModelStoreOwner` parameter instead. " + + "This function might be removed in the future. " + + "Make sure you call this function with named arguments please so your source still compile when this is removed." +) +@Composable +inline fun viewModel( + key: String? = null, + noinline initializer: CreationExtras.() -> VM +): VM = + viewModel(defaultViewModelStoreOwner(), key, initializer) diff --git a/lifecycle-viewmodel/src/composeUiMain/kotlin/ViewModel.composeUi.kt b/lifecycle-viewmodel/src/composeUiMain/kotlin/ViewModel.composeUi.kt index 955b18e7..8bf5f498 100644 --- a/lifecycle-viewmodel/src/composeUiMain/kotlin/ViewModel.composeUi.kt +++ b/lifecycle-viewmodel/src/composeUiMain/kotlin/ViewModel.composeUi.kt @@ -2,9 +2,33 @@ package com.huanshankeji.androidx.lifecycle.viewmodel.compose import androidx.compose.runtime.Composable import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelStoreOwner import androidx.lifecycle.viewmodel.CreationExtras -import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner +import kotlin.reflect.KClass +import androidx.lifecycle.viewmodel.compose.viewModel as composeUiViewModel +// copied and adapted from "ViewModel.kt" in `androidx.lifecycle.viewmodel.compose` +@PublishedApi @Composable -actual inline fun viewModel(key: String?, noinline initializer: CreationExtras.() -> VM): VM = - viewModel(key = key, initializer = initializer) +internal actual fun defaultViewModelStoreOwner(): ViewModelStoreOwner = + checkNotNull(LocalViewModelStoreOwner.current) { + "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner" + } + +@Composable +actual fun viewModel( + modelClass: KClass, + viewModelStoreOwner: ViewModelStoreOwner, + key: String?, + factory: ViewModelProvider.Factory?, + extras: CreationExtras +): VM = + composeUiViewModel(modelClass, viewModelStoreOwner, key, factory, extras) + +@Composable +actual inline fun viewModel( + viewModelStoreOwner: ViewModelStoreOwner, key: String?, noinline initializer: CreationExtras.() -> VM +): VM = + composeUiViewModel(viewModelStoreOwner, key, initializer) diff --git a/lifecycle-viewmodel/src/jsMain/kotlin/LocalViewModelStoreOwner.kt b/lifecycle-viewmodel/src/jsMain/kotlin/LocalViewModelStoreOwner.kt new file mode 100644 index 00000000..f84bd0a9 --- /dev/null +++ b/lifecycle-viewmodel/src/jsMain/kotlin/LocalViewModelStoreOwner.kt @@ -0,0 +1,26 @@ +package com.huanshankeji.androidx.lifecycle.viewmodel.compose + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.ProvidedValue +import androidx.compose.runtime.compositionLocalOf +import androidx.lifecycle.ViewModelStoreOwner + +// copied and adapted from "LocalViewModelStoreOwner.kt" and "LocalViewModelStoreOwner.jb.kt" in `androidx.lifecycle.viewmodel.compose` + +object LocalViewModelStoreOwner { + private val LocalViewModelStoreOwner = + compositionLocalOf { null } + val current: ViewModelStoreOwner? + @Composable + get() = LocalViewModelStoreOwner.current ?: findViewTreeViewModelStoreOwner() + + infix fun provides(viewModelStoreOwner: ViewModelStoreOwner): + ProvidedValue { + return LocalViewModelStoreOwner.provides(viewModelStoreOwner) + } +} + +@Composable +internal fun findViewTreeViewModelStoreOwner(): ViewModelStoreOwner? = + // TODO + null //findComposeDefaultViewModelStoreOwner() diff --git a/lifecycle-viewmodel/src/jsMain/kotlin/ViewModel.js.kt b/lifecycle-viewmodel/src/jsMain/kotlin/ViewModel.js.kt index ba52ce07..1f372f54 100644 --- a/lifecycle-viewmodel/src/jsMain/kotlin/ViewModel.js.kt +++ b/lifecycle-viewmodel/src/jsMain/kotlin/ViewModel.js.kt @@ -1,10 +1,74 @@ package com.huanshankeji.androidx.lifecycle.viewmodel.compose import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember +import androidx.lifecycle.HasDefaultViewModelProviderFactory import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelStoreOwner import androidx.lifecycle.viewmodel.CreationExtras +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import kotlin.reflect.KClass + +// copied and adapted from "ViewModel.kt" in `androidx.lifecycle.viewmodel.compose` + + + +@PublishedApi +@Composable +internal actual fun defaultViewModelStoreOwner(): ViewModelStoreOwner = + checkNotNull(LocalViewModelStoreOwner.current) { + "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner" + } + @Composable -actual inline fun viewModel(key: String?, noinline initializer: CreationExtras.() -> VM): VM = +actual fun viewModel( + modelClass: KClass, + viewModelStoreOwner: ViewModelStoreOwner, + key: String?, + factory: ViewModelProvider.Factory?, + extras: CreationExtras +): VM = viewModelStoreOwner.get(modelClass, key, factory, extras) + +@Composable +actual inline fun viewModel( + viewModelStoreOwner: ViewModelStoreOwner, + key: String?, + noinline initializer: CreationExtras.() -> VM +): VM = viewModel( + VM::class, + viewModelStoreOwner, + key, + viewModelFactory { initializer(initializer) }, + viewModelStoreOwner.defaultCreationExtras() +) + +// TODO remove +/* +@Composable +actual inline fun viewModel( + viewModelStoreOwner: ViewModelStoreOwner, key: String?, noinline initializer: CreationExtras.() -> VM +): VM = remember(key) { CreationExtras.Empty.initializer() } +*/ + +internal fun ViewModelStoreOwner.get( + modelClass: KClass, + key: String?, + factory: ViewModelProvider.Factory?, + extras: CreationExtras +): VM { + val provider = if (factory != null) { + ViewModelProvider.create(this.viewModelStore, factory, extras) + } else if (this is HasDefaultViewModelProviderFactory) { + ViewModelProvider.create(this.viewModelStore, this.defaultViewModelProviderFactory, extras) + } else { + ViewModelProvider.create(this) + } + return if (key != null) { + provider[key, modelClass] + } else { + provider[modelClass] + } +} From c005afb24646753d29b989c3d34c3f18834e6170 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Tue, 7 Jan 2025 09:33:53 +0800 Subject: [PATCH 04/16] Remove unneeded code and adjust the code format to be consistent --- .../src/composeUiMain/kotlin/ViewModel.composeUi.kt | 4 +++- lifecycle-viewmodel/src/jsMain/kotlin/ViewModel.js.kt | 9 --------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/lifecycle-viewmodel/src/composeUiMain/kotlin/ViewModel.composeUi.kt b/lifecycle-viewmodel/src/composeUiMain/kotlin/ViewModel.composeUi.kt index 8bf5f498..8dd26b59 100644 --- a/lifecycle-viewmodel/src/composeUiMain/kotlin/ViewModel.composeUi.kt +++ b/lifecycle-viewmodel/src/composeUiMain/kotlin/ViewModel.composeUi.kt @@ -29,6 +29,8 @@ actual fun viewModel( @Composable actual inline fun viewModel( - viewModelStoreOwner: ViewModelStoreOwner, key: String?, noinline initializer: CreationExtras.() -> VM + viewModelStoreOwner: ViewModelStoreOwner, + key: String?, + noinline initializer: CreationExtras.() -> VM ): VM = composeUiViewModel(viewModelStoreOwner, key, initializer) diff --git a/lifecycle-viewmodel/src/jsMain/kotlin/ViewModel.js.kt b/lifecycle-viewmodel/src/jsMain/kotlin/ViewModel.js.kt index 1f372f54..a7bd921c 100644 --- a/lifecycle-viewmodel/src/jsMain/kotlin/ViewModel.js.kt +++ b/lifecycle-viewmodel/src/jsMain/kotlin/ViewModel.js.kt @@ -44,15 +44,6 @@ actual inline fun viewModel( viewModelStoreOwner.defaultCreationExtras() ) -// TODO remove -/* -@Composable -actual inline fun viewModel( - viewModelStoreOwner: ViewModelStoreOwner, key: String?, noinline initializer: CreationExtras.() -> VM -): VM = - remember(key) { CreationExtras.Empty.initializer() } -*/ - internal fun ViewModelStoreOwner.get( modelClass: KClass, key: String?, From 0209d158953ce5c5758e7238646dcaa42a8a6b2a Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Tue, 7 Jan 2025 12:11:02 +0800 Subject: [PATCH 05/16] Uncomment and update the ViewModel code in the "common" module, add the necessary dependencies, and update the demo `Material3` composable to use a `Material3ViewModel` to demonstrate ViewModels An exception is thrown with the message "ViewModelStore should be set before setGraph call" on JS DOM in an internal app when navigating back. --- common/build.gradle.kts | 3 +-- .../ui/platform/DefaultViewModelOwnerStore.kt | 2 -- .../compose/ui/window/ComposeWindow.js.kt | 22 +++++++++++++------ demo/build.gradle.kts | 1 + .../compose/material/demo/Material3.kt | 12 ++++++---- .../material/demo/Material3ViewModel.kt | 9 ++++++++ .../compose/material/demo/Main.kt | 5 +++-- lifecycle-viewmodel/build.gradle.kts | 4 ++++ .../src/commonMain/kotlin/ViewModel.kt | 3 ++- .../jsMain/kotlin/LocalViewModelStoreOwner.kt | 6 +++-- 10 files changed, 47 insertions(+), 20 deletions(-) create mode 100644 demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material3ViewModel.kt diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 56cd8b8c..770c49d1 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -57,13 +57,12 @@ kotlin { api("com.varabyte.kobweb:kobweb-compose:${DependencyVersions.kobweb}") implementation("com.huanshankeji:compose-html-common:${DependencyVersions.huanshankejiComposeHtml}") - // TODO not used yet /* The UI module depends on the lifecycle module to use `androidx.lifecycle.ViewModelStoreOwner`. See https://github.com/JetBrains/compose-multiplatform-core/blob/jb-main/compose/ui/ui/build.gradle#L87. This is actually only needed for JS DOM. */ - //implementation(cpnProject(project, ":lifecycle-viewmodel")) + implementation(commonDependencies.jetbrainsAndroidx.lifecycle.viewmodel()) } } } diff --git a/common/src/jsMain/kotlin/com/huanshankeji/compose/ui/platform/DefaultViewModelOwnerStore.kt b/common/src/jsMain/kotlin/com/huanshankeji/compose/ui/platform/DefaultViewModelOwnerStore.kt index 1bdcdee8..e56f1e29 100644 --- a/common/src/jsMain/kotlin/com/huanshankeji/compose/ui/platform/DefaultViewModelOwnerStore.kt +++ b/common/src/jsMain/kotlin/com/huanshankeji/compose/ui/platform/DefaultViewModelOwnerStore.kt @@ -1,6 +1,5 @@ package com.huanshankeji.compose.ui.platform -/* // copied and adapted from "DefaultViewModelOwnerStore.skiko.kt" in `androidx.compose.ui.platform` import androidx.compose.runtime.Composable @@ -22,4 +21,3 @@ internal val LocalInternalViewModelStoreOwner = staticCompositionLocalOf.() -> Unit ): Composition = renderComposableInBody { // copied and adapted from `ComposeWindow` in "ComposeWindow.web.kt" in `androidx.compose.ui.window` // also see `ComposeViewport` on Wasm JS + @OptIn(ExperimentalApi::class) CompositionLocalProvider( - //LocalSystemTheme provides systemThemeObserver.currentSystemTheme.value, // TODO add back if needed one day - //LocalLifecycleOwner provides this, // TODO - LocalInternalViewModelStoreOwner provides TODO(), + /* TODO add back these 2 lines below if needed one day + in a function possibly named `renderComposableInBodyWithLifecycle` */ + //LocalSystemTheme provides systemThemeObserver.currentSystemTheme.value, + //LocalLifecycleOwner provides this, + LocalInternalViewModelStoreOwner provides SimpleViewModelStoreOwner(), content = { content() } ) } -*/ diff --git a/demo/build.gradle.kts b/demo/build.gradle.kts index b1699b89..d25c0535 100644 --- a/demo/build.gradle.kts +++ b/demo/build.gradle.kts @@ -49,6 +49,7 @@ kotlin { implementation(compose.runtime) implementation(cpnProject(project, ":material2")) implementation(cpnProject(project, ":material3")) + implementation(cpnProject(project, ":lifecycle-viewmodel")) implementation(cpnProject(project, ":navigation")) /* see https://github.com/JetBrains/compose-multiplatform-core/blob/476d43b99a27696d12ef087e8028d90789645ba7/compose/ui/ui/build.gradle#L54 diff --git a/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material3.kt b/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material3.kt index a32c2d6e..606063b9 100644 --- a/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material3.kt +++ b/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material3.kt @@ -2,6 +2,7 @@ package com.huanshankeji.compose.material.demo import androidx.compose.runtime.* import androidx.compose.ui.unit.dp +import com.huanshankeji.androidx.lifecycle.viewmodel.compose.viewModel import com.huanshankeji.compose.ExtRecommendedApi import com.huanshankeji.compose.foundation.layout.* import com.huanshankeji.compose.foundation.rememberScrollState @@ -27,10 +28,12 @@ import com.huanshankeji.compose.ui.Modifier import com.huanshankeji.compose.material3.Button as RowScopeButton @Composable -fun Material3(/*modifier: Modifier = Modifier*/) { +fun Material3(/*modifier: Modifier = Modifier*/ + viewModel: Material3ViewModel = viewModel { Material3ViewModel() } +) { Column(Modifier.verticalScroll(rememberScrollState()).innerContentPadding(), Arrangement.spacedBy(16.dp)) { - var count by remember { mutableStateOf(0) } - val onClick: () -> Unit = { count++ } + val count by viewModel.countState.collectAsState() + val onClick: () -> Unit = { viewModel.countState.value++ } val buttonContent: @Composable () -> Unit = { TaglessText(count.toString()) } @@ -56,7 +59,8 @@ fun Material3(/*modifier: Modifier = Modifier*/) { FilledTonalIconButton(onClick, content = iconButtonContent) OutlinedIconButton(onClick, content = iconButtonContent) } - val (checked, onCheckedChange) = remember { mutableStateOf(false) } + val checked = viewModel.checkedState.collectAsState().value + val onCheckedChange: (Boolean) -> Unit = { viewModel.checkedState.value = it } val iconToggleButtonContent: @Composable () -> Unit = { Icon(if (checked) Icons.Default.Add else Icons.Default.Remove, null) } diff --git a/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material3ViewModel.kt b/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material3ViewModel.kt new file mode 100644 index 00000000..f45f7a6d --- /dev/null +++ b/demo/src/commonMain/kotlin/com/huanshankeji/compose/material/demo/Material3ViewModel.kt @@ -0,0 +1,9 @@ +package com.huanshankeji.compose.material.demo + +import androidx.lifecycle.ViewModel +import kotlinx.coroutines.flow.MutableStateFlow + +class Material3ViewModel : ViewModel() { + val countState = MutableStateFlow(0) + val checkedState = MutableStateFlow(false) +} \ No newline at end of file diff --git a/demo/src/jsMain/kotlin/com/huanshankeji/compose/material/demo/Main.kt b/demo/src/jsMain/kotlin/com/huanshankeji/compose/material/demo/Main.kt index 8766b2c7..0007736a 100644 --- a/demo/src/jsMain/kotlin/com/huanshankeji/compose/material/demo/Main.kt +++ b/demo/src/jsMain/kotlin/com/huanshankeji/compose/material/demo/Main.kt @@ -1,9 +1,10 @@ package com.huanshankeji.compose.material.demo import com.huanshankeji.compose.html.material3.require -import org.jetbrains.compose.web.renderComposableInBody +import com.huanshankeji.compose.ui.window.renderComposableInBodyWithViewModelStoreOwner fun main() { require("material-symbols/outlined.css") - renderComposableInBody { App() } + //renderComposableInBody { App() } // "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner" + renderComposableInBodyWithViewModelStoreOwner { App() } } diff --git a/lifecycle-viewmodel/build.gradle.kts b/lifecycle-viewmodel/build.gradle.kts index 38114e06..de4754f1 100644 --- a/lifecycle-viewmodel/build.gradle.kts +++ b/lifecycle-viewmodel/build.gradle.kts @@ -1,3 +1,4 @@ +import com.huanshankeji.cpnProject import com.huanshankeji.team.`Shreck Ye` import com.huanshankeji.team.pomForTeamDefaultOpenSource @@ -16,6 +17,9 @@ kotlin { */ api(compose.runtime) api(commonDependencies.jetbrainsAndroidx.lifecycle.viewmodel()) + // only needed on JS DOM actually + // https://github.com/JetBrains/compose-multiplatform-core/blob/f1e03d0784631a88201931a6a6a708cdd090be57/lifecycle/lifecycle-viewmodel-compose/build.gradle#L58 + api(cpnProject(project, ":common")) } } composeUiMain { diff --git a/lifecycle-viewmodel/src/commonMain/kotlin/ViewModel.kt b/lifecycle-viewmodel/src/commonMain/kotlin/ViewModel.kt index 3057449f..5576acea 100644 --- a/lifecycle-viewmodel/src/commonMain/kotlin/ViewModel.kt +++ b/lifecycle-viewmodel/src/commonMain/kotlin/ViewModel.kt @@ -46,7 +46,8 @@ expect inline fun viewModel( @Deprecated( "Use the one with a `viewModelStoreOwner` parameter instead. " + "This function might be removed in the future. " + - "Make sure you call this function with named arguments please so your source still compile when this is removed." + "If you call this function with a `key` argument, make sure you used a named argument " + + "so your source still compiles when this is removed." ) @Composable inline fun viewModel( diff --git a/lifecycle-viewmodel/src/jsMain/kotlin/LocalViewModelStoreOwner.kt b/lifecycle-viewmodel/src/jsMain/kotlin/LocalViewModelStoreOwner.kt index f84bd0a9..fb362152 100644 --- a/lifecycle-viewmodel/src/jsMain/kotlin/LocalViewModelStoreOwner.kt +++ b/lifecycle-viewmodel/src/jsMain/kotlin/LocalViewModelStoreOwner.kt @@ -1,9 +1,11 @@ package com.huanshankeji.androidx.lifecycle.viewmodel.compose import androidx.compose.runtime.Composable +import androidx.compose.runtime.InternalComposeApi import androidx.compose.runtime.ProvidedValue import androidx.compose.runtime.compositionLocalOf import androidx.lifecycle.ViewModelStoreOwner +import com.huanshankeji.compose.ui.platform.findComposeDefaultViewModelStoreOwner // copied and adapted from "LocalViewModelStoreOwner.kt" and "LocalViewModelStoreOwner.jb.kt" in `androidx.lifecycle.viewmodel.compose` @@ -20,7 +22,7 @@ object LocalViewModelStoreOwner { } } +@OptIn(InternalComposeApi::class) @Composable internal fun findViewTreeViewModelStoreOwner(): ViewModelStoreOwner? = - // TODO - null //findComposeDefaultViewModelStoreOwner() + findComposeDefaultViewModelStoreOwner() From 3d49fd5a57c9d3430eee1e20540870256212b86d Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Tue, 7 Jan 2025 19:59:01 +0800 Subject: [PATCH 06/16] Rename a file --- ...ultViewModelOwnerStore.kt => DefaultViewModelOwnerStore.js.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename common/src/jsMain/kotlin/com/huanshankeji/compose/ui/platform/{DefaultViewModelOwnerStore.kt => DefaultViewModelOwnerStore.js.kt} (100%) diff --git a/common/src/jsMain/kotlin/com/huanshankeji/compose/ui/platform/DefaultViewModelOwnerStore.kt b/common/src/jsMain/kotlin/com/huanshankeji/compose/ui/platform/DefaultViewModelOwnerStore.js.kt similarity index 100% rename from common/src/jsMain/kotlin/com/huanshankeji/compose/ui/platform/DefaultViewModelOwnerStore.kt rename to common/src/jsMain/kotlin/com/huanshankeji/compose/ui/platform/DefaultViewModelOwnerStore.js.kt From 4337249fe0746d9f8d3dc586d7f67dd0ac56624c Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Tue, 7 Jan 2025 22:05:03 +0800 Subject: [PATCH 07/16] Uncomment `LocalViewModelStoreOwner.current` in `NavHost` on JS DOM and the issue in commit 0209d158953ce5c5758e7238646dcaa42a8a6b2a is resolved --- navigation/build.gradle.kts | 2 ++ .../com/huanshankeji/androidx/navigation/compose/NavHost.js.kt | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/navigation/build.gradle.kts b/navigation/build.gradle.kts index 0ad712fe..c41c7932 100644 --- a/navigation/build.gradle.kts +++ b/navigation/build.gradle.kts @@ -20,6 +20,8 @@ kotlin { //implementation("org.jetbrains.compose.annotation-internal:annotation:${DependencyVersions.composeMultiplatform}") api(cpnProject(project, ":common")) // for `Modifier` and `Alignment` //implementation("org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose:2.8.0") // This depends on Compose target '[jscanvas]'. + // https://github.com/JetBrains/compose-multiplatform-core/blob/f6d989a1ae9cd5895b4fba7821946ead389c4848/navigation/navigation-compose/build.gradle#L58 + api(cpnProject(project, ":lifecycle-viewmodel")) } } composeUiMain { diff --git a/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt b/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt index 1f334f8d..c8c1831b 100644 --- a/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt +++ b/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt @@ -4,6 +4,7 @@ import androidx.compose.runtime.* import androidx.lifecycle.ViewModelStore import androidx.lifecycle.ViewModelStoreOwner import androidx.navigation.* +import com.huanshankeji.androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner import com.huanshankeji.compose.foundation.layout.Box import com.huanshankeji.compose.foundation.layout.fillMaxSize import com.huanshankeji.compose.ui.Alignment @@ -56,7 +57,7 @@ actual fun NavHost( ) { //val lifecycleOwner = LocalLifecycleOwner.current - val viewModelStoreOwner = /*LocalViewModelStoreOwner.current ?:*/ rememberViewModelStoreOwner() + val viewModelStoreOwner = LocalViewModelStoreOwner.current ?: rememberViewModelStoreOwner() navController.setViewModelStore(viewModelStoreOwner.viewModelStore) From 5a3a3a9fcb222bccd75cdbafcb361d8be821fb4e Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Tue, 7 Jan 2025 23:27:09 +0800 Subject: [PATCH 08/16] Update README.md about the ViewModel updates --- README.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4858815a..99c9ecf9 100644 --- a/README.md +++ b/README.md @@ -143,15 +143,13 @@ The `com.huanshankeji.compose.material.icons.Icon` class delegates to both kinds ### ViewModel -The ViewModel module currently supports a small subset of the Compose ViewModel APIs, and delegates to raw UI state on -Compose HTML / JS DOM. These APIs are highly experimental now. +The ViewModel module currently supports a subset of the Compose ViewModel APIs. For ViewModel to work properly on Compose HTML / JS DOM, call `com.huanshankeji.compose.ui.window.renderComposableInBodyWithViewModelStoreOwner` instead of `org.jetbrains.compose.web.renderComposableInBody` on JS. These APIs are experimental now. ### Navigation The navigation module currently supports a small subset of the Compose Navigation APIs, which does not support -transition or animation on Compose HTML / JS DOM. These APIs are also highly experimental now. -See [CMP-4966](https://youtrack.jetbrains.com/issue/CMP-4966) for a bug to avoid. Also, ViewModel-related functions -are not implemented yet on Compose HTML / JS DOM. +transition or animation on Compose HTML / JS DOM. These APIs are also experimental now. +See [CMP-4966](https://youtrack.jetbrains.com/issue/CMP-4966) for a bug to avoid. ## Add to your dependencies From 1114458f4a9567a440ee15694a7e17c9a60c4dfb Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 8 Jan 2025 08:39:00 +0800 Subject: [PATCH 09/16] Review `NavHost` in "NavHost.kt" in `androidx.navigation.compose` to see whether there is more ViewModel-related code to copy and adapt here, and merge its changes since last time --- .../androidx/navigation/compose/NavHost.js.kt | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt b/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt index c8c1831b..2ec9929c 100644 --- a/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt +++ b/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt @@ -66,21 +66,19 @@ actual fun NavHost( // Find the ComposeNavigator, returning early if it isn't found // (such as is the case when using TestNavHostController) - val composeNavigator = navController.navigatorProvider.get>( - ComposeNavigator.NAME - ) as? ComposeNavigator ?: return + val composeNavigator = + navController.navigatorProvider.get>(ComposeNavigator.NAME) + as? ComposeNavigator ?: return //val currentBackStack by composeNavigator.backStack.collectAsState() -// BackHandler(currentBackStack.size > 1) { -// navController.popBackStack() -// } + // `progress`, `isPredictiveBack`, etc. /* DisposableEffect(lifecycleOwner) { // Setup the navController with proper owners navController.setLifecycleOwner(lifecycleOwner) - onDispose { } + onDispose {} } */ @@ -100,6 +98,13 @@ actual fun NavHost( val backStackEntry: NavBackStackEntry? = visibleEntries.lastOrNull() if (backStackEntry != null) { + // TODO remove this if not needed + DisposableEffect(true) { + onDispose { + visibleEntries.forEach { entry -> composeNavigator.onTransitionComplete(entry) } + } + } + // `fillMaxSize` is added here to make the Box align to the size of its parent // TODO consider adding a version of `NavHost` without `modifier` and `contentAlignment` // Originally it was `transition.AnimatedContent` here. @@ -110,8 +115,9 @@ actual fun NavHost( // while in the scope of the composable, we provide the navBackStackEntry as the // ViewModelStoreOwner and LifecycleOwner currentEntry?.LocalOwnersProvider(saveableStateHolder) { - (currentEntry.destination as ComposeNavigator.Destination) - .content(currentEntry) + (currentEntry.destination as ComposeNavigator.Destination).content( + currentEntry + ) } */ currentEntry?.let { @@ -120,19 +126,18 @@ actual fun NavHost( } } + // TODO remove this if not needed DisposableEffect(true) { onDispose { - visibleEntries.forEach { entry -> - composeNavigator.onTransitionComplete(entry) - } + visibleEntries.forEach { entry -> composeNavigator.onTransitionComplete(entry) } } } } /* - val dialogNavigator = navController.navigatorProvider.get>( - DialogNavigator.NAME - ) as? DialogNavigator ?: return + val dialogNavigator = + navController.navigatorProvider.get>(DialogNavigator.NAME) + as? DialogNavigator ?: return // Show any dialog destinations DialogHost(dialogNavigator) From bd650c19d2ff11e97a86c0c9a55e9e91108937b0 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 8 Jan 2025 08:56:02 +0800 Subject: [PATCH 10/16] Remove code related to `composeNavigator` in `NavHost` since it seems also related to animations and transitions, and navigation is manually tested to work without it --- .../androidx/navigation/compose/NavHost.js.kt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt b/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt index 2ec9929c..ad7db99d 100644 --- a/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt +++ b/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt @@ -64,15 +64,18 @@ actual fun NavHost( // Then set the graph navController.graph = graph + // This seems not needed here since it seems also related to animations and transitions. (comment added when adapting from Compose UI `NavHost) + /* // Find the ComposeNavigator, returning early if it isn't found // (such as is the case when using TestNavHostController) val composeNavigator = navController.navigatorProvider.get>(ComposeNavigator.NAME) as? ComposeNavigator ?: return + */ //val currentBackStack by composeNavigator.backStack.collectAsState() - // `progress`, `isPredictiveBack`, etc. + // `progress`, `isPredictiveBack`, etc. (comment added when adapting from Compose UI `NavHost) /* DisposableEffect(lifecycleOwner) { @@ -98,12 +101,13 @@ actual fun NavHost( val backStackEntry: NavBackStackEntry? = visibleEntries.lastOrNull() if (backStackEntry != null) { - // TODO remove this if not needed + /* DisposableEffect(true) { onDispose { visibleEntries.forEach { entry -> composeNavigator.onTransitionComplete(entry) } } } + */ // `fillMaxSize` is added here to make the Box align to the size of its parent // TODO consider adding a version of `NavHost` without `modifier` and `contentAlignment` @@ -126,12 +130,13 @@ actual fun NavHost( } } - // TODO remove this if not needed + /* DisposableEffect(true) { onDispose { visibleEntries.forEach { entry -> composeNavigator.onTransitionComplete(entry) } } } + */ } /* From e710860a51ffc25f6cb6ac0e80957ac9637e657d Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 8 Jan 2025 09:08:13 +0800 Subject: [PATCH 11/16] Run `apiDump` --- ...multiplatform-html-unified-common.klib.api | 20 +++++++++++++++++++ ...tform-html-unified-lifecycle-viewmodel.api | 9 +++++++++ ...-html-unified-lifecycle-viewmodel.klib.api | 18 +++++++++++++++++ ...tform-html-unified-lifecycle-viewmodel.api | 9 +++++++++ 4 files changed, 56 insertions(+) diff --git a/common/api/compose-multiplatform-html-unified-common.klib.api b/common/api/compose-multiplatform-html-unified-common.klib.api index 97ff7cf5..5a02a3d9 100644 --- a/common/api/compose-multiplatform-html-unified-common.klib.api +++ b/common/api/compose-multiplatform-html-unified-common.klib.api @@ -992,6 +992,14 @@ final enum class com.huanshankeji.browser/Browser : kotlin/Enum // com.huanshankeji.browser/Browser.values|values#static(){}[0] } +// Targets: [js] +final class com.huanshankeji.compose.ui.window/SimpleViewModelStoreOwner : androidx.lifecycle/ViewModelStoreOwner { // com.huanshankeji.compose.ui.window/SimpleViewModelStoreOwner|null[0] + constructor () // com.huanshankeji.compose.ui.window/SimpleViewModelStoreOwner.|(){}[0] + + final val viewModelStore // com.huanshankeji.compose.ui.window/SimpleViewModelStoreOwner.viewModelStore|{}viewModelStore[0] + final fun (): androidx.lifecycle/ViewModelStore // com.huanshankeji.compose.ui.window/SimpleViewModelStoreOwner.viewModelStore.|(){}[0] +} + // Targets: [js] final object com.huanshankeji.compose.foundation.lazy/LazyItemScope { // com.huanshankeji.compose.foundation.lazy/LazyItemScope|null[0] final fun (com.huanshankeji.compose.ui/Modifier).fillParentMaxHeight(kotlin/Float = ...): com.huanshankeji.compose.ui/Modifier // com.huanshankeji.compose.foundation.lazy/LazyItemScope.fillParentMaxHeight|fillParentMaxHeight@com.huanshankeji.compose.ui.Modifier(kotlin.Float){}[0] @@ -1028,6 +1036,9 @@ final val com.huanshankeji.compose.foundation/imitateComposeUiLayoutHorizontalSc final val com.huanshankeji.compose.foundation/imitateComposeUiLayoutVerticalScrollPlatformModifier // com.huanshankeji.compose.foundation/imitateComposeUiLayoutVerticalScrollPlatformModifier|{}imitateComposeUiLayoutVerticalScrollPlatformModifier[0] final fun (): com.varabyte.kobweb.compose.ui/Modifier // com.huanshankeji.compose.foundation/imitateComposeUiLayoutVerticalScrollPlatformModifier.|(){}[0] +// Targets: [js] +final val com.huanshankeji.compose.ui.window/com_huanshankeji_compose_ui_window_SimpleViewModelStoreOwner$stableprop // com.huanshankeji.compose.ui.window/com_huanshankeji_compose_ui_window_SimpleViewModelStoreOwner$stableprop|#static{}com_huanshankeji_compose_ui_window_SimpleViewModelStoreOwner$stableprop[0] + // Targets: [js] final fun (androidx.compose.ui.unit/Dp).com.huanshankeji.compose.ui.unit/toPx(): org.jetbrains.compose.web.css/CSSSizeValue // com.huanshankeji.compose.ui.unit/toPx|toPx@androidx.compose.ui.unit.Dp(){}[0] @@ -1138,3 +1149,12 @@ final fun com.huanshankeji.compose.foundation/com_huanshankeji_compose_foundatio // Targets: [js] final fun com.huanshankeji.compose.foundation/rememberScrollState(kotlin/Int, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): com.huanshankeji.compose.foundation/ScrollState // com.huanshankeji.compose.foundation/rememberScrollState|rememberScrollState(kotlin.Int;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0] + +// Targets: [js] +final fun com.huanshankeji.compose.ui.platform/findComposeDefaultViewModelStoreOwner(androidx.compose.runtime/Composer?, kotlin/Int): androidx.lifecycle/ViewModelStoreOwner? // com.huanshankeji.compose.ui.platform/findComposeDefaultViewModelStoreOwner|findComposeDefaultViewModelStoreOwner(androidx.compose.runtime.Composer?;kotlin.Int){}[0] + +// Targets: [js] +final fun com.huanshankeji.compose.ui.window/com_huanshankeji_compose_ui_window_SimpleViewModelStoreOwner$stableprop_getter(): kotlin/Int // com.huanshankeji.compose.ui.window/com_huanshankeji_compose_ui_window_SimpleViewModelStoreOwner$stableprop_getter|com_huanshankeji_compose_ui_window_SimpleViewModelStoreOwner$stableprop_getter(){}[0] + +// Targets: [js] +final fun com.huanshankeji.compose.ui.window/renderComposableInBodyWithViewModelStoreOwner(kotlin/Function3, androidx.compose.runtime/Composer, kotlin/Int, kotlin/Unit>): androidx.compose.runtime/Composition // com.huanshankeji.compose.ui.window/renderComposableInBodyWithViewModelStoreOwner|renderComposableInBodyWithViewModelStoreOwner(kotlin.Function3,androidx.compose.runtime.Composer,kotlin.Int,kotlin.Unit>){}[0] diff --git a/lifecycle-viewmodel/api/android/compose-multiplatform-html-unified-lifecycle-viewmodel.api b/lifecycle-viewmodel/api/android/compose-multiplatform-html-unified-lifecycle-viewmodel.api index e69de29b..819e2a1a 100644 --- a/lifecycle-viewmodel/api/android/compose-multiplatform-html-unified-lifecycle-viewmodel.api +++ b/lifecycle-viewmodel/api/android/compose-multiplatform-html-unified-lifecycle-viewmodel.api @@ -0,0 +1,9 @@ +public final class com/huanshankeji/androidx/lifecycle/viewmodel/compose/ViewModelKt { + public static final fun defaultCreationExtras (Landroidx/lifecycle/ViewModelStoreOwner;)Landroidx/lifecycle/viewmodel/CreationExtras; +} + +public final class com/huanshankeji/androidx/lifecycle/viewmodel/compose/ViewModel_composeUiKt { + public static final fun defaultViewModelStoreOwner (Landroidx/compose/runtime/Composer;I)Landroidx/lifecycle/ViewModelStoreOwner; + public static final fun viewModel (Lkotlin/reflect/KClass;Landroidx/lifecycle/ViewModelStoreOwner;Ljava/lang/String;Landroidx/lifecycle/ViewModelProvider$Factory;Landroidx/lifecycle/viewmodel/CreationExtras;Landroidx/compose/runtime/Composer;II)Landroidx/lifecycle/ViewModel; +} + diff --git a/lifecycle-viewmodel/api/compose-multiplatform-html-unified-lifecycle-viewmodel.klib.api b/lifecycle-viewmodel/api/compose-multiplatform-html-unified-lifecycle-viewmodel.klib.api index 8678f89a..d662a05e 100644 --- a/lifecycle-viewmodel/api/compose-multiplatform-html-unified-lifecycle-viewmodel.klib.api +++ b/lifecycle-viewmodel/api/compose-multiplatform-html-unified-lifecycle-viewmodel.klib.api @@ -6,4 +6,22 @@ // - Show declarations: true // Library unique name: +final fun (androidx.lifecycle/ViewModelStoreOwner).com.huanshankeji.androidx.lifecycle.viewmodel.compose/defaultCreationExtras(): androidx.lifecycle.viewmodel/CreationExtras // com.huanshankeji.androidx.lifecycle.viewmodel.compose/defaultCreationExtras|defaultCreationExtras@androidx.lifecycle.ViewModelStoreOwner(){}[0] +final fun <#A: androidx.lifecycle/ViewModel> com.huanshankeji.androidx.lifecycle.viewmodel.compose/viewModel(kotlin.reflect/KClass<#A>, androidx.lifecycle/ViewModelStoreOwner?, kotlin/String?, androidx.lifecycle/ViewModelProvider.Factory?, androidx.lifecycle.viewmodel/CreationExtras?, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): #A // com.huanshankeji.androidx.lifecycle.viewmodel.compose/viewModel|viewModel(kotlin.reflect.KClass<0:0>;androidx.lifecycle.ViewModelStoreOwner?;kotlin.String?;androidx.lifecycle.ViewModelProvider.Factory?;androidx.lifecycle.viewmodel.CreationExtras?;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){0§}[0] +final fun com.huanshankeji.androidx.lifecycle.viewmodel.compose/defaultViewModelStoreOwner(androidx.compose.runtime/Composer?, kotlin/Int): androidx.lifecycle/ViewModelStoreOwner // com.huanshankeji.androidx.lifecycle.viewmodel.compose/defaultViewModelStoreOwner|defaultViewModelStoreOwner(androidx.compose.runtime.Composer?;kotlin.Int){}[0] +final inline fun <#A: reified androidx.lifecycle/ViewModel> com.huanshankeji.androidx.lifecycle.viewmodel.compose/viewModel(androidx.lifecycle/ViewModelStoreOwner?, kotlin/String?, noinline kotlin/Function1, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): #A // com.huanshankeji.androidx.lifecycle.viewmodel.compose/viewModel|viewModel(androidx.lifecycle.ViewModelStoreOwner?;kotlin.String?;kotlin.Function1;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){0§}[0] final inline fun <#A: reified androidx.lifecycle/ViewModel> com.huanshankeji.androidx.lifecycle.viewmodel.compose/viewModel(kotlin/String?, noinline kotlin/Function1, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int): #A // com.huanshankeji.androidx.lifecycle.viewmodel.compose/viewModel|viewModel(kotlin.String?;kotlin.Function1;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){0§}[0] + +// Targets: [js] +final object com.huanshankeji.androidx.lifecycle.viewmodel.compose/LocalViewModelStoreOwner { // com.huanshankeji.androidx.lifecycle.viewmodel.compose/LocalViewModelStoreOwner|null[0] + final val current // com.huanshankeji.androidx.lifecycle.viewmodel.compose/LocalViewModelStoreOwner.current|{}current[0] + final fun (androidx.compose.runtime/Composer?, kotlin/Int): androidx.lifecycle/ViewModelStoreOwner? // com.huanshankeji.androidx.lifecycle.viewmodel.compose/LocalViewModelStoreOwner.current.|(androidx.compose.runtime.Composer?;kotlin.Int){}[0] + + final fun provides(androidx.lifecycle/ViewModelStoreOwner): androidx.compose.runtime/ProvidedValue // com.huanshankeji.androidx.lifecycle.viewmodel.compose/LocalViewModelStoreOwner.provides|provides(androidx.lifecycle.ViewModelStoreOwner){}[0] +} + +// Targets: [js] +final val com.huanshankeji.androidx.lifecycle.viewmodel.compose/com_huanshankeji_androidx_lifecycle_viewmodel_compose_LocalViewModelStoreOwner$stableprop // com.huanshankeji.androidx.lifecycle.viewmodel.compose/com_huanshankeji_androidx_lifecycle_viewmodel_compose_LocalViewModelStoreOwner$stableprop|#static{}com_huanshankeji_androidx_lifecycle_viewmodel_compose_LocalViewModelStoreOwner$stableprop[0] + +// Targets: [js] +final fun com.huanshankeji.androidx.lifecycle.viewmodel.compose/com_huanshankeji_androidx_lifecycle_viewmodel_compose_LocalViewModelStoreOwner$stableprop_getter(): kotlin/Int // com.huanshankeji.androidx.lifecycle.viewmodel.compose/com_huanshankeji_androidx_lifecycle_viewmodel_compose_LocalViewModelStoreOwner$stableprop_getter|com_huanshankeji_androidx_lifecycle_viewmodel_compose_LocalViewModelStoreOwner$stableprop_getter(){}[0] diff --git a/lifecycle-viewmodel/api/jvm/compose-multiplatform-html-unified-lifecycle-viewmodel.api b/lifecycle-viewmodel/api/jvm/compose-multiplatform-html-unified-lifecycle-viewmodel.api index e69de29b..819e2a1a 100644 --- a/lifecycle-viewmodel/api/jvm/compose-multiplatform-html-unified-lifecycle-viewmodel.api +++ b/lifecycle-viewmodel/api/jvm/compose-multiplatform-html-unified-lifecycle-viewmodel.api @@ -0,0 +1,9 @@ +public final class com/huanshankeji/androidx/lifecycle/viewmodel/compose/ViewModelKt { + public static final fun defaultCreationExtras (Landroidx/lifecycle/ViewModelStoreOwner;)Landroidx/lifecycle/viewmodel/CreationExtras; +} + +public final class com/huanshankeji/androidx/lifecycle/viewmodel/compose/ViewModel_composeUiKt { + public static final fun defaultViewModelStoreOwner (Landroidx/compose/runtime/Composer;I)Landroidx/lifecycle/ViewModelStoreOwner; + public static final fun viewModel (Lkotlin/reflect/KClass;Landroidx/lifecycle/ViewModelStoreOwner;Ljava/lang/String;Landroidx/lifecycle/ViewModelProvider$Factory;Landroidx/lifecycle/viewmodel/CreationExtras;Landroidx/compose/runtime/Composer;II)Landroidx/lifecycle/ViewModel; +} + From 72bbfb84c7bcdb250f3865469c6bdd33ae04b881 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 8 Jan 2025 09:10:29 +0800 Subject: [PATCH 12/16] Revert "Update the project version to be branch-specific" This reverts commit ebfa280ed3f23196fdc2fadba78f1d1a379be05c. --- buildSrc/src/main/kotlin/VersionsAndDependencies.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index d3927b3a..7720bc25 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -1,7 +1,7 @@ import com.huanshankeji.CommonDependencies import org.jetbrains.compose.ComposeBuildConfig -val projectVersion = "0.5.1-save-viewmodel-in-navigation-on-js-dom-SNAPSHOT" +val projectVersion = "0.5.1-SNAPSHOT" val commonDependencies = CommonDependencies() From c0dad3108b2c53c11c89636e03ff1a8d7a50e6e6 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 8 Jan 2025 09:11:29 +0800 Subject: [PATCH 13/16] Bump the project version to 0.6.0-SNAPSHOT --- buildSrc/src/main/kotlin/VersionsAndDependencies.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt index 7720bc25..bb4a72c4 100644 --- a/buildSrc/src/main/kotlin/VersionsAndDependencies.kt +++ b/buildSrc/src/main/kotlin/VersionsAndDependencies.kt @@ -1,7 +1,7 @@ import com.huanshankeji.CommonDependencies import org.jetbrains.compose.ComposeBuildConfig -val projectVersion = "0.5.1-SNAPSHOT" +val projectVersion = "0.6.0-SNAPSHOT" val commonDependencies = CommonDependencies() From 96935b6f10fc936069ec99e02761dc9c21c93843 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 8 Jan 2025 09:16:15 +0800 Subject: [PATCH 14/16] Reformat the navigation section in README.md --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 99c9ecf9..c815f808 100644 --- a/README.md +++ b/README.md @@ -147,9 +147,7 @@ The ViewModel module currently supports a subset of the Compose ViewModel APIs. ### Navigation -The navigation module currently supports a small subset of the Compose Navigation APIs, which does not support -transition or animation on Compose HTML / JS DOM. These APIs are also experimental now. -See [CMP-4966](https://youtrack.jetbrains.com/issue/CMP-4966) for a bug to avoid. +The navigation module currently supports a small subset of the Compose Navigation APIs, which does not support transition or animation on Compose HTML / JS DOM. These APIs are also experimental now. See [CMP-4966](https://youtrack.jetbrains.com/issue/CMP-4966) for a bug to avoid. ## Add to your dependencies From 7f4928b67b79bd1dcae8c9bbfb404f9d7d3d315f Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 8 Jan 2025 10:13:51 +0800 Subject: [PATCH 15/16] Apply suggestions from code review --- .../huanshankeji/androidx/navigation/compose/NavHost.js.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt b/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt index ad7db99d..d77353a4 100644 --- a/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt +++ b/navigation/src/jsMain/kotlin/com/huanshankeji/androidx/navigation/compose/NavHost.js.kt @@ -64,7 +64,7 @@ actual fun NavHost( // Then set the graph navController.graph = graph - // This seems not needed here since it seems also related to animations and transitions. (comment added when adapting from Compose UI `NavHost) + // This seems not needed here since it seems also related to animations and transitions. (comment added when adapting from Compose UI `NavHost`) /* // Find the ComposeNavigator, returning early if it isn't found // (such as is the case when using TestNavHostController) @@ -75,7 +75,7 @@ actual fun NavHost( //val currentBackStack by composeNavigator.backStack.collectAsState() - // `progress`, `isPredictiveBack`, etc. (comment added when adapting from Compose UI `NavHost) + // `progress`, `isPredictiveBack`, etc. (comment added when adapting from Compose UI `NavHost`) /* DisposableEffect(lifecycleOwner) { From 1f1e30b0201d0b2865b6309a528dafb5562840d6 Mon Sep 17 00:00:00 2001 From: Yongshun Shreck Ye Date: Wed, 8 Jan 2025 10:16:44 +0800 Subject: [PATCH 16/16] Remove the "lifecycle-viewmodel" module dependency in the "demo" module since the "navigation" module depends on the "lifecycle-viewmodel" via api now --- demo/build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/demo/build.gradle.kts b/demo/build.gradle.kts index d25c0535..b1699b89 100644 --- a/demo/build.gradle.kts +++ b/demo/build.gradle.kts @@ -49,7 +49,6 @@ kotlin { implementation(compose.runtime) implementation(cpnProject(project, ":material2")) implementation(cpnProject(project, ":material3")) - implementation(cpnProject(project, ":lifecycle-viewmodel")) implementation(cpnProject(project, ":navigation")) /* see https://github.com/JetBrains/compose-multiplatform-core/blob/476d43b99a27696d12ef087e8028d90789645ba7/compose/ui/ui/build.gradle#L54