From 0f25bf6645387ead77280ceafdc4376625d950d4 Mon Sep 17 00:00:00 2001 From: Thiago dos Santos Date: Sat, 21 Mar 2026 14:18:14 -0300 Subject: [PATCH 01/20] feat: cmp 1.10.3 and kotlin 2.2.21 --- .editorconfig | 5 +- .../configuration/configure-ktlint.gradle.kts | 1 - build.gradle.kts | 12 ++-- gradle.properties | 1 - gradle/libs.versions.toml | 31 +++++----- includes.gradle.kts | 14 ++--- samples/multiplatform/build.gradle.kts | 6 +- .../build.gradle.kts | 4 +- voyager-core/build.gradle.kts | 4 +- voyager-kodein/build.gradle.kts | 62 ++++++++++++++++++- voyager-koin/build.gradle.kts | 4 +- voyager-lifecycle-kmp/build.gradle.kts | 7 +-- voyager-navigator/build.gradle.kts | 4 +- voyager-screenmodel/build.gradle.kts | 4 +- voyager-tab-navigator/build.gradle.kts | 4 +- voyager-transitions/build.gradle.kts | 2 +- 16 files changed, 112 insertions(+), 53 deletions(-) diff --git a/.editorconfig b/.editorconfig index 74c7bcd9..88f32646 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,4 +4,7 @@ root = true charset = utf-8 insert_final_newline = true max_line_length = 120 -end_of_line = crlf \ No newline at end of file +end_of_line = crlf + +[*.{kt,kts}] +ktlint_function_naming_ignore_when_annotated_with = Composable \ No newline at end of file diff --git a/build-config/src/main/kotlin/configuration/configure-ktlint.gradle.kts b/build-config/src/main/kotlin/configuration/configure-ktlint.gradle.kts index 14f9b0b9..66430b22 100644 --- a/build-config/src/main/kotlin/configuration/configure-ktlint.gradle.kts +++ b/build-config/src/main/kotlin/configuration/configure-ktlint.gradle.kts @@ -6,5 +6,4 @@ configure { val libs = extensions.getByType().named("libs") version = libs.findVersion("ktlint").get().toString() - disabledRules = setOf("filename") } diff --git a/build.gradle.kts b/build.gradle.kts index 46cfb021..f4c96b87 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,12 +19,12 @@ plugins { apiValidation { with(ignoredProjects) { - add(projects.samples.android) - add(projects.samples.multiplatform) - add(projects.samples.multiModule.app) - add(projects.samples.multiModule.featureHome) - add(projects.samples.multiModule.featurePosts) - add(projects.samples.multiModule.navigation) +// add(projects.samples.android) +// add(projects.samples.multiplatform) +// add(projects.samples.multiModule.app) +// add(projects.samples.multiModule.featureHome) +// add(projects.samples.multiModule.featurePosts) +// add(projects.samples.multiModule.navigation) } with(nonPublicMarkers) { add("cafe.adriel.voyager.core.annotation.InternalVoyagerApi") diff --git a/gradle.properties b/gradle.properties index 91bf78ea..3cf16b7c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,6 @@ android.library.defaults.buildfeatures.androidresources=false # Kotlin kotlin.code.style=official -kotlin.native.useEmbeddableCompilerJar=true kotlin.native.ignoreDisabledTargets=true kotlin.mpp.stability.nowarn=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 93fb631a..958ee65f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,23 +1,22 @@ [versions] -agp = "8.11.0" -kotlin = "2.2.0" +agp = "8.12.2" +kotlin = "2.2.21" mavenPublish = "0.33.0" -composeMultiplatform = "1.8.2" -binaryCompatibilityValidator = "0.18.0" +composeMultiplatform = "1.10.3" +binaryCompatibilityValidator = "0.18.1" atomicfu = "0.29.0" -ktlint = "0.47.1" -ktlintPlugin = "12.3.0" +ktlint = "1.0.0" +ktlintPlugin = "13.0.0" coroutines = "1.10.2" -kodein = "7.21.2" -koin-compose = "4.1.0" +kodein = "7.29.0" +koin-compose = "4.1.1" hilt = "2.56.2" -lifecycle = "2.9.1" -activityCompose = "1.10.1" -rxjava = "3.1.10" +lifecycle = "2.10.0" +activityCompose = "1.12.0" +rxjava = "3.1.12" -androidxKmp-bundle = "1.0.1" -androidxKmp-lifecycle = "2.9.1" +androidxKmp-lifecycle = "2.10.0" junit = "5.13.2" @@ -41,10 +40,14 @@ androidx-lifecycle-livedata = { module = "androidx.lifecycle:lifecycle-livedata- androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" } rxjava = { module = "io.reactivex.rxjava3:rxjava", version.ref = "rxjava" } -androidxKmp-core-bundle = { module = "org.jetbrains.androidx.core:core-bundle", version.ref = "androidxKmp-bundle" } +androidxKmp-animation = { module = "org.jetbrains.compose.animation:animation", version.ref = "composeMultiplatform" } androidxKmp-lifecycle-runtimeCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidxKmp-lifecycle" } androidxKmp-lifecycle-viewmodel = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel", version.ref = "androidxKmp-lifecycle" } androidxKmp-lifecycle-viewmodelCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidxKmp-lifecycle" } +androidxKmp-material = { module = "org.jetbrains.compose.material:material", version.ref = "composeMultiplatform" } +androidxKmp-runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "composeMultiplatform" } +androidxKmp-runtime-saveable = { module = "org.jetbrains.compose.runtime:runtime-saveable", version.ref = "composeMultiplatform" } +androidxKmp-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "composeMultiplatform" } junit-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" } junit-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" } diff --git a/includes.gradle.kts b/includes.gradle.kts index 4d521b14..dd710807 100644 --- a/includes.gradle.kts +++ b/includes.gradle.kts @@ -1,11 +1,11 @@ include( - ":samples:android", - ":samples:multiplatform", - - ":samples:multi-module:app", - ":samples:multi-module:navigation", - ":samples:multi-module:feature-home", - ":samples:multi-module:feature-posts", +// ":samples:android", +// ":samples:multiplatform", +// +// ":samples:multi-module:app", +// ":samples:multi-module:navigation", +// ":samples:multi-module:feature-home", +// ":samples:multi-module:feature-posts", ":voyager-core", ":voyager-screenmodel", diff --git a/samples/multiplatform/build.gradle.kts b/samples/multiplatform/build.gradle.kts index 990bcacd..2741397e 100644 --- a/samples/multiplatform/build.gradle.kts +++ b/samples/multiplatform/build.gradle.kts @@ -83,9 +83,9 @@ kotlin { implementation(projects.voyagerCore) implementation(projects.voyagerNavigator) implementation(projects.voyagerTransitions) - implementation(compose.runtime) - implementation(compose.ui) - implementation(compose.material) + implementation(libs.androidxKmp.runtime) + implementation(libs.androidxKmp.ui) + implementation(libs.androidxKmp.material) implementation(libs.coroutines.core) } androidMain.dependencies { diff --git a/voyager-bottom-sheet-navigator/build.gradle.kts b/voyager-bottom-sheet-navigator/build.gradle.kts index cf0e0e89..2d6f2957 100644 --- a/voyager-bottom-sheet-navigator/build.gradle.kts +++ b/voyager-bottom-sheet-navigator/build.gradle.kts @@ -15,8 +15,8 @@ kotlin { commonMain.dependencies { api(projects.voyagerCore) api(projects.voyagerNavigator) - implementation(compose.runtime) - implementation(compose.material) + implementation(libs.androidxKmp.runtime) + implementation(libs.androidxKmp.material) } androidMain.dependencies { implementation(libs.androidx.activity.compose) diff --git a/voyager-core/build.gradle.kts b/voyager-core/build.gradle.kts index 53dce871..0e194d52 100644 --- a/voyager-core/build.gradle.kts +++ b/voyager-core/build.gradle.kts @@ -14,8 +14,8 @@ android { kotlin { sourceSets { commonMain.dependencies { - implementation(compose.runtime) - implementation(compose.runtimeSaveable) + implementation(libs.androidxKmp.runtime) + implementation(libs.androidxKmp.runtime.saveable) implementation(libs.coroutines.core) } commonJvmTest.dependencies { diff --git a/voyager-kodein/build.gradle.kts b/voyager-kodein/build.gradle.kts index 4a57a47c..77f55f4a 100644 --- a/voyager-kodein/build.gradle.kts +++ b/voyager-kodein/build.gradle.kts @@ -1,9 +1,16 @@ +@file:OptIn(ExperimentalKotlinGradlePluginApi::class) + +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.mpp.KotlinAndroidTarget + plugins { alias(libs.plugins.android.library) alias(libs.plugins.kotlin.multiplatform) alias(libs.plugins.kotlin.compose) alias(libs.plugins.compose.multiplatform) - id("voyager-kmp-module") + id("voyager-base-module") } android { @@ -11,14 +18,63 @@ android { } kotlin { + applyDefaultHierarchyTemplate { + common { + group("commonJvm") { + withCompilations { + it.target.targetName == "desktop" || it.target is KotlinAndroidTarget + } + } + group("nonAndroid") { + withJs() + withNative() + withWasmJs() + withCompilations { + it.target.targetName == "desktop" + } + } + group("commonWeb") { + withJs() + withWasmJs() + } + } + } + + js(IR) { + browser() + } + @OptIn(ExperimentalWasmDsl::class) + wasmJs { + browser() + } + + androidTarget { + compilerOptions { + jvmTarget = JvmTarget.JVM_11 + } + } + jvm("desktop") { + compilerOptions { + jvmTarget = JvmTarget.JVM_11 + } + } + + iosArm64() + iosX64() + iosSimulatorArm64() + + compilerOptions { + freeCompilerArgs.add("-Xexpect-actual-classes") + } + sourceSets { commonMain.dependencies { api(projects.voyagerCore) api(projects.voyagerScreenmodel) api(projects.voyagerNavigator) implementation(libs.kodein) - implementation(compose.runtime) - implementation(compose.runtimeSaveable) + implementation(libs.androidxKmp.runtime) + implementation(libs.androidxKmp.runtime.saveable) } } } diff --git a/voyager-koin/build.gradle.kts b/voyager-koin/build.gradle.kts index 4f9a00f3..a842f4b6 100644 --- a/voyager-koin/build.gradle.kts +++ b/voyager-koin/build.gradle.kts @@ -17,8 +17,8 @@ kotlin { api(projects.voyagerScreenmodel) api(projects.voyagerNavigator) - implementation(compose.runtime) - implementation(compose.runtimeSaveable) + implementation(libs.androidxKmp.runtime) + implementation(libs.androidxKmp.runtime.saveable) implementation(libs.coroutines.core) } diff --git a/voyager-lifecycle-kmp/build.gradle.kts b/voyager-lifecycle-kmp/build.gradle.kts index 53fa3155..ae19efe8 100644 --- a/voyager-lifecycle-kmp/build.gradle.kts +++ b/voyager-lifecycle-kmp/build.gradle.kts @@ -15,16 +15,15 @@ kotlin { commonMain.dependencies { api(projects.voyagerCore) api(projects.voyagerNavigator) - implementation(compose.runtime) - implementation(compose.runtimeSaveable) + implementation(libs.androidxKmp.runtime) + implementation(libs.androidxKmp.runtime.saveable) implementation(libs.androidxKmp.lifecycle.viewmodelCompose) implementation(libs.androidxKmp.lifecycle.viewmodel) implementation(libs.androidxKmp.lifecycle.runtimeCompose) - implementation(libs.androidxKmp.core.bundle) } androidMain.dependencies { implementation(libs.androidx.lifecycle.savedState) - implementation(compose.ui) + implementation(libs.androidxKmp.ui) } } } diff --git a/voyager-navigator/build.gradle.kts b/voyager-navigator/build.gradle.kts index 72635bc3..655fd17b 100644 --- a/voyager-navigator/build.gradle.kts +++ b/voyager-navigator/build.gradle.kts @@ -14,8 +14,8 @@ kotlin { sourceSets { commonMain.dependencies { api(projects.voyagerCore) - implementation(compose.runtime) - implementation(compose.runtimeSaveable) + implementation(libs.androidxKmp.runtime) + implementation(libs.androidxKmp.runtime.saveable) } androidMain.dependencies { implementation(libs.androidx.activity.compose) diff --git a/voyager-screenmodel/build.gradle.kts b/voyager-screenmodel/build.gradle.kts index 11391cd2..c838788b 100644 --- a/voyager-screenmodel/build.gradle.kts +++ b/voyager-screenmodel/build.gradle.kts @@ -15,8 +15,8 @@ kotlin { commonMain.dependencies { api(projects.voyagerCore) api(projects.voyagerNavigator) - implementation(compose.runtime) - implementation(compose.runtimeSaveable) + implementation(libs.androidxKmp.runtime) + implementation(libs.androidxKmp.runtime.saveable) implementation(libs.coroutines.core) } } diff --git a/voyager-tab-navigator/build.gradle.kts b/voyager-tab-navigator/build.gradle.kts index 592156fe..a231f2ca 100644 --- a/voyager-tab-navigator/build.gradle.kts +++ b/voyager-tab-navigator/build.gradle.kts @@ -15,8 +15,8 @@ kotlin { commonMain.dependencies { api(projects.voyagerCore) api(projects.voyagerNavigator) - implementation(compose.runtime) - implementation(compose.ui) + implementation(libs.androidxKmp.runtime) + implementation(libs.androidxKmp.ui) } } } diff --git a/voyager-transitions/build.gradle.kts b/voyager-transitions/build.gradle.kts index 4e993f97..6e49b655 100644 --- a/voyager-transitions/build.gradle.kts +++ b/voyager-transitions/build.gradle.kts @@ -15,7 +15,7 @@ kotlin { commonMain.dependencies { api(projects.voyagerCore) api(projects.voyagerNavigator) - implementation(compose.animation) + implementation(libs.androidxKmp.animation) } } } From a7483e5bcc07caa3a449b65f4b0f3ac7b07e598d Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Sat, 21 Mar 2026 14:51:52 -0300 Subject: [PATCH 02/20] fix: ktlint voyager-core --- .gitignore | 2 + gradle/libs.versions.toml | 2 +- .../androidx/AndroidScreenLifecycleOwner.kt | 312 +++++++++--------- .../core/lifecycle/ConfigurationChecker.kt | 11 +- .../core/lifecycle/Serializable.jvm.kt | 2 + .../core/stack/SnapshotStateStackTest.kt | 39 ++- .../adriel/voyager/core/utils/Quadruple.kt | 2 +- .../core/annotation/InternalVoyagerApi.kt | 12 +- .../voyager/core/concurrent/ThreadSafeList.kt | 40 ++- .../voyager/core/concurrent/ThreadSafeMap.kt | 14 +- .../voyager/core/concurrent/ThreadSafeSet.kt | 10 + .../voyager/core/internal/SafeCollections.kt | 3 +- .../core/lifecycle/DisposableEffect.kt | 10 +- .../core/lifecycle/LifecycleEffectStore.kt | 11 +- ...kt => MultipleScreenLifecycleOwnerUtil.kt} | 12 +- .../lifecycle/NavigatorScreenLifecycle.kt | 1 - .../voyager/core/lifecycle/ScreenLifecycle.kt | 18 +- .../core/lifecycle/ScreenLifecycleOwner.kt | 2 +- .../core/lifecycle/ScreenLifecycleStore.kt | 9 +- .../voyager/core/lifecycle/Serializable.kt | 2 + .../voyager/core/registry/ScreenModule.kt | 3 +- .../voyager/core/registry/ScreenRegistry.kt | 6 +- .../cafe/adriel/voyager/core/screen/Screen.kt | 1 - .../voyager/core/stack/SnapshotStateStack.kt | 29 +- .../cafe/adriel/voyager/core/stack/Stack.kt | 6 +- .../concurrent/AtomicInt32.js.kt | 1 + .../concurrent/ThreadSafeList.js.kt | 23 +- .../concurrent/ThreadSafeMap.js.kt | 8 +- .../concurrent/ThreadSafeSet.js.kt | 3 +- .../lifecycle/Serializable.native.kt | 2 + .../concurrent/AtomicInt32.native.kt | 1 + .../concurrent/ThreadSafeList.native.kt | 22 +- .../concurrent/ThreadSafeMap.native.kt | 8 +- .../concurrent/ThreadSafeMutableCollection.kt | 3 +- .../concurrent/ThreadSafeMutableIterator.kt | 5 +- .../concurrent/ThreadSafeSet.native.kt | 2 +- .../lifecycle/Serializable.native.kt | 2 + 37 files changed, 366 insertions(+), 273 deletions(-) rename voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/lifecycle/{multipleScreenLifecycleOwnerUtil.kt => MultipleScreenLifecycleOwnerUtil.kt} (90%) diff --git a/.gitignore b/.gitignore index f93288af..c42215a2 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,5 @@ google-services.json # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* replay_pid* + +*.DS_Store diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 958ee65f..ef25c809 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ mavenPublish = "0.33.0" composeMultiplatform = "1.10.3" binaryCompatibilityValidator = "0.18.1" atomicfu = "0.29.0" -ktlint = "1.0.0" +ktlint = "1.0.1" ktlintPlugin = "13.0.0" coroutines = "1.10.2" diff --git a/voyager-core/src/androidMain/kotlin/cafe/adriel/voyager/androidx/AndroidScreenLifecycleOwner.kt b/voyager-core/src/androidMain/kotlin/cafe/adriel/voyager/androidx/AndroidScreenLifecycleOwner.kt index bf21ef26..49108582 100644 --- a/voyager-core/src/androidMain/kotlin/cafe/adriel/voyager/androidx/AndroidScreenLifecycleOwner.kt +++ b/voyager-core/src/androidMain/kotlin/cafe/adriel/voyager/androidx/AndroidScreenLifecycleOwner.kt @@ -45,200 +45,206 @@ public class AndroidScreenLifecycleOwner private constructor() : ViewModelStoreOwner, SavedStateRegistryOwner, HasDefaultViewModelProviderFactory { + override val lifecycle: LifecycleRegistry = LifecycleRegistry(this) - override val lifecycle: LifecycleRegistry = LifecycleRegistry(this) + override val viewModelStore: ViewModelStore = ViewModelStore() - override val viewModelStore: ViewModelStore = ViewModelStore() + private val atomicAppContext = AtomicReference() + internal val atomicParentLifecycleOwner = AtomicReference() - private val atomicAppContext = AtomicReference() - internal val atomicParentLifecycleOwner = AtomicReference() + private val controller = SavedStateRegistryController.create(this) - private val controller = SavedStateRegistryController.create(this) + private var isCreated: Boolean by mutableStateOf(false) - private var isCreated: Boolean by mutableStateOf(false) + override val savedStateRegistry: SavedStateRegistry + get() = controller.savedStateRegistry - override val savedStateRegistry: SavedStateRegistry - get() = controller.savedStateRegistry + override val defaultViewModelProviderFactory: ViewModelProvider.Factory + get() = + SavedStateViewModelFactory( + application = atomicAppContext.get()?.getApplication(), + owner = this, + ) - override val defaultViewModelProviderFactory: ViewModelProvider.Factory - get() = SavedStateViewModelFactory( - application = atomicAppContext.get()?.getApplication(), - owner = this - ) - - override val defaultViewModelCreationExtras: CreationExtras - get() = MutableCreationExtras().apply { - val application = atomicAppContext.get()?.getApplication() - if (application != null) { - set(AndroidViewModelFactory.APPLICATION_KEY, application) - } - set(SAVED_STATE_REGISTRY_OWNER_KEY, this@AndroidScreenLifecycleOwner) - set(VIEW_MODEL_STORE_OWNER_KEY, this@AndroidScreenLifecycleOwner) + override val defaultViewModelCreationExtras: CreationExtras + get() = + MutableCreationExtras().apply { + val application = atomicAppContext.get()?.getApplication() + if (application != null) { + set(AndroidViewModelFactory.APPLICATION_KEY, application) + } + set(SAVED_STATE_REGISTRY_OWNER_KEY, this@AndroidScreenLifecycleOwner) + set(VIEW_MODEL_STORE_OWNER_KEY, this@AndroidScreenLifecycleOwner) /* TODO if (getArguments() != null) { extras.set(DEFAULT_ARGS_KEY, getArguments()) }*/ - } - - init { - controller.performAttach() - enableSavedStateHandles() - } - - private fun onCreate(savedState: Bundle?) { - check(!isCreated) { "onCreate already called" } - isCreated = true - controller.performRestore(savedState) - initEvents.forEach { - lifecycle.safeHandleLifecycleEvent(it) - } - } - - private fun emitOnStartEvents() { - startEvents.forEach { - lifecycle.safeHandleLifecycleEvent(it) - } - } + } - private fun emitOnStopEvents() { - stopEvents.forEach { - lifecycle.safeHandleLifecycleEvent(it) + init { + controller.performAttach() + enableSavedStateHandles() } - } - - @Composable - override fun ProvideBeforeScreenContent( - provideSaveableState: @Composable (suffixKey: String, content: @Composable () -> Unit) -> Unit, - content: @Composable () -> Unit - ) { - provideSaveableState("lifecycle") { - LifecycleDisposableEffect() - val hooks = getHooks() - - CompositionLocalProvider(*hooks.toTypedArray()) { - content() + private fun onCreate(savedState: Bundle?) { + check(!isCreated) { "onCreate already called" } + isCreated = true + controller.performRestore(savedState) + initEvents.forEach { + lifecycle.safeHandleLifecycleEvent(it) } } - } - override fun onDispose(screen: Screen) { - viewModelStore.clear() - disposeEvents.forEach { event -> - lifecycle.safeHandleLifecycleEvent(event) + private fun emitOnStartEvents() { + startEvents.forEach { + lifecycle.safeHandleLifecycleEvent(it) + } } - } - - private fun performSave(outState: Bundle) { - controller.performSave(outState) - } - @Composable - private fun getHooks(): List> { - atomicAppContext.compareAndSet(null, LocalContext.current.applicationContext) - atomicParentLifecycleOwner.compareAndSet(null, LocalLifecycleOwner.current) - - return remember(this) { - listOf( - LocalLifecycleOwner provides this, - LocalViewModelStoreOwner provides this, - LocalSavedStateRegistryOwner provides this - ) + private fun emitOnStopEvents() { + stopEvents.forEach { + lifecycle.safeHandleLifecycleEvent(it) + } } - } - /** - * Returns a unregister callback - */ - private fun registerLifecycleListener(outState: Bundle): () -> Unit { - val lifecycleOwner = atomicParentLifecycleOwner.get() - if (lifecycleOwner != null) { - val observer = object : DefaultLifecycleObserver { - override fun onPause(owner: LifecycleOwner) { - lifecycle.safeHandleLifecycleEvent(Lifecycle.Event.ON_PAUSE) - } - - override fun onResume(owner: LifecycleOwner) { - lifecycle.safeHandleLifecycleEvent(Lifecycle.Event.ON_RESUME) - } + @Composable + override fun ProvideBeforeScreenContent( + provideSaveableState: @Composable (suffixKey: String, content: @Composable () -> Unit) -> Unit, + content: @Composable () -> Unit, + ) { + provideSaveableState("lifecycle") { + LifecycleDisposableEffect() - override fun onStart(owner: LifecycleOwner) { - lifecycle.safeHandleLifecycleEvent(Lifecycle.Event.ON_START) - } - - override fun onStop(owner: LifecycleOwner) { - lifecycle.safeHandleLifecycleEvent(Lifecycle.Event.ON_STOP) + val hooks = getHooks() - // when the Application goes to background, perform save - performSave(outState) + CompositionLocalProvider(*hooks.toTypedArray()) { + content() } } - val lifecycle = lifecycleOwner.lifecycle - lifecycle.addObserver(observer) - - return { lifecycle.removeObserver(observer) } - } else { - return { } } - } - @Composable - private fun LifecycleDisposableEffect() { - val savedState = rememberSaveable { Bundle() } - if (!isCreated) { - onCreate(savedState) // do this in the UI thread to force it to be called before anything else + override fun onDispose(screen: Screen) { + viewModelStore.clear() + disposeEvents.forEach { event -> + lifecycle.safeHandleLifecycleEvent(event) + } } - DisposableEffect(this) { - val unregisterLifecycle = registerLifecycleListener(savedState) - emitOnStartEvents() - - onDispose { - unregisterLifecycle() + private fun performSave(outState: Bundle) { + controller.performSave(outState) + } - // when the screen goes to stack, perform save - performSave(savedState) + @Composable + private fun getHooks(): List> { + atomicAppContext.compareAndSet(null, LocalContext.current.applicationContext) + atomicParentLifecycleOwner.compareAndSet(null, LocalLifecycleOwner.current) + + return remember(this) { + listOf( + LocalLifecycleOwner provides this, + LocalViewModelStoreOwner provides this, + LocalSavedStateRegistryOwner provides this, + ) + } + } - // notify lifecycle screen listeners - emitOnStopEvents() + /** + * Returns a unregister callback + */ + private fun registerLifecycleListener(outState: Bundle): () -> Unit { + val lifecycleOwner = atomicParentLifecycleOwner.get() + if (lifecycleOwner != null) { + val observer = + object : DefaultLifecycleObserver { + override fun onPause(owner: LifecycleOwner) { + lifecycle.safeHandleLifecycleEvent(Lifecycle.Event.ON_PAUSE) + } + + override fun onResume(owner: LifecycleOwner) { + lifecycle.safeHandleLifecycleEvent(Lifecycle.Event.ON_RESUME) + } + + override fun onStart(owner: LifecycleOwner) { + lifecycle.safeHandleLifecycleEvent(Lifecycle.Event.ON_START) + } + + override fun onStop(owner: LifecycleOwner) { + lifecycle.safeHandleLifecycleEvent(Lifecycle.Event.ON_STOP) + + // when the Application goes to background, perform save + performSave(outState) + } + } + val lifecycle = lifecycleOwner.lifecycle + lifecycle.addObserver(observer) + + return { lifecycle.removeObserver(observer) } + } else { + return { } } } - } - private fun Context.getApplication(): Application? = when (this) { - is Application -> this - else -> null - } + @Composable + private fun LifecycleDisposableEffect() { + val savedState = rememberSaveable { Bundle() } + if (!isCreated) { + onCreate(savedState) // do this in the UI thread to force it to be called before anything else + } - private fun LifecycleRegistry.safeHandleLifecycleEvent(event: Lifecycle.Event) { - val currentState = currentState - if (!currentState.isAtLeast(Lifecycle.State.INITIALIZED)) return + DisposableEffect(this) { + val unregisterLifecycle = registerLifecycleListener(savedState) + emitOnStartEvents() - handleLifecycleEvent(event) - } + onDispose { + unregisterLifecycle() - public companion object { + // when the screen goes to stack, perform save + performSave(savedState) - private val initEvents = arrayOf( - Lifecycle.Event.ON_CREATE - ) + // notify lifecycle screen listeners + emitOnStopEvents() + } + } + } - private val startEvents = arrayOf( - Lifecycle.Event.ON_START, - Lifecycle.Event.ON_RESUME - ) + private fun Context.getApplication(): Application? = + when (this) { + is Application -> this + else -> null + } - private val stopEvents = arrayOf( - Lifecycle.Event.ON_PAUSE, - Lifecycle.Event.ON_STOP - ) + private fun LifecycleRegistry.safeHandleLifecycleEvent(event: Lifecycle.Event) { + val currentState = currentState + if (!currentState.isAtLeast(Lifecycle.State.INITIALIZED)) return - private val disposeEvents = arrayOf( - Lifecycle.Event.ON_DESTROY - ) + handleLifecycleEvent(event) + } - public fun get(screen: Screen): ScreenLifecycleOwner { - return ScreenLifecycleStore.get(screen) { AndroidScreenLifecycleOwner() } + public companion object { + private val initEvents = + arrayOf( + Lifecycle.Event.ON_CREATE, + ) + + private val startEvents = + arrayOf( + Lifecycle.Event.ON_START, + Lifecycle.Event.ON_RESUME, + ) + + private val stopEvents = + arrayOf( + Lifecycle.Event.ON_PAUSE, + Lifecycle.Event.ON_STOP, + ) + + private val disposeEvents = + arrayOf( + Lifecycle.Event.ON_DESTROY, + ) + + public fun get(screen: Screen): ScreenLifecycleOwner { + return ScreenLifecycleStore.get(screen) { AndroidScreenLifecycleOwner() } + } } } -} diff --git a/voyager-core/src/androidMain/kotlin/cafe/adriel/voyager/core/lifecycle/ConfigurationChecker.kt b/voyager-core/src/androidMain/kotlin/cafe/adriel/voyager/core/lifecycle/ConfigurationChecker.kt index 3ca7c7a5..377ddcac 100644 --- a/voyager-core/src/androidMain/kotlin/cafe/adriel/voyager/core/lifecycle/ConfigurationChecker.kt +++ b/voyager-core/src/androidMain/kotlin/cafe/adriel/voyager/core/lifecycle/ConfigurationChecker.kt @@ -9,11 +9,12 @@ import androidx.compose.runtime.Stable import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalContext -private tailrec fun Context.getActivity(): Activity? = when (this) { - is Activity -> this - is ContextWrapper -> baseContext.getActivity() - else -> null -} +private tailrec fun Context.getActivity(): Activity? = + when (this) { + is Activity -> this + is ContextWrapper -> baseContext.getActivity() + else -> null + } @Composable @NonRestartableComposable diff --git a/voyager-core/src/commonJvmMain/kotlin/cafe/adriel/voyager/core/lifecycle/Serializable.jvm.kt b/voyager-core/src/commonJvmMain/kotlin/cafe/adriel/voyager/core/lifecycle/Serializable.jvm.kt index b01e2845..4295c82f 100644 --- a/voyager-core/src/commonJvmMain/kotlin/cafe/adriel/voyager/core/lifecycle/Serializable.jvm.kt +++ b/voyager-core/src/commonJvmMain/kotlin/cafe/adriel/voyager/core/lifecycle/Serializable.jvm.kt @@ -1,3 +1,5 @@ +@file:Suppress("ktlint:standard:filename") + package cafe.adriel.voyager.core.lifecycle import java.io.Serializable diff --git a/voyager-core/src/commonJvmTest/kotlin/cafe/adriel/voyager/core/stack/SnapshotStateStackTest.kt b/voyager-core/src/commonJvmTest/kotlin/cafe/adriel/voyager/core/stack/SnapshotStateStackTest.kt index 94fb6c33..927b939a 100644 --- a/voyager-core/src/commonJvmTest/kotlin/cafe/adriel/voyager/core/stack/SnapshotStateStackTest.kt +++ b/voyager-core/src/commonJvmTest/kotlin/cafe/adriel/voyager/core/stack/SnapshotStateStackTest.kt @@ -8,12 +8,11 @@ import org.junit.jupiter.api.assertDoesNotThrow import org.junit.jupiter.api.assertThrows internal class SnapshotStateStackTest { - @TestFactory fun initSingle(): Sequence = sequenceOf( "item 1" to 0, - "item 1" to 1 + "item 1" to 1, ).map { (item, minSize) -> dynamicTest("when min size is $minSize then should not throw") { assertDoesNotThrow { @@ -30,11 +29,11 @@ internal class SnapshotStateStackTest { Triple(listOf("item 1"), 0, false), Triple(listOf("item 1"), 1, false), Triple(listOf("item 1", "item 2"), 0, false), - Triple(listOf("item 1", "item 2"), 1, false) + Triple(listOf("item 1", "item 2"), 1, false), ).map { (items, minSize, throws) -> dynamicTest( "when items size is ${items.size} and min size is $minSize " + - "then should ${if (throws) "" else "not"} throw" + "then should ${if (throws) "" else "not"} throw", ) { if (throws) { assertThrows { @@ -53,7 +52,7 @@ internal class SnapshotStateStackTest { sequenceOf( emptyList(), listOf("item 1"), - listOf("item 1", "item 2") + listOf("item 1", "item 2"), ).map { items -> dynamicTest("when items size is ${items.size} then return same content") { val stack = SnapshotStateStack(items) @@ -67,7 +66,7 @@ internal class SnapshotStateStackTest { sequenceOf( Triple(emptyList(), null, false), Triple(listOf("item 1"), "item 1", true), - Triple(listOf("item 1", "item 2"), "item 2", true) + Triple(listOf("item 1", "item 2"), "item 2", true), ).map { (items, expected, success) -> dynamicTest("when items size is ${items.size} then return ${if (success) "last" else "null"}") { val stack = SnapshotStateStack(items) @@ -81,7 +80,7 @@ internal class SnapshotStateStackTest { sequenceOf( emptyList() to 0, listOf("item 1") to 1, - listOf("item 1", "item 2") to 2 + listOf("item 1", "item 2") to 2, ).map { (items, expected) -> dynamicTest("when items size is ${items.size} then return $expected") { val stack = SnapshotStateStack(items) @@ -95,7 +94,7 @@ internal class SnapshotStateStackTest { sequenceOf( emptyList() to true, listOf("item 1") to false, - listOf("item 1", "item 2") to false + listOf("item 1", "item 2") to false, ).map { (items, expected) -> dynamicTest("when items size is ${items.size} then return $expected") { val stack = SnapshotStateStack(items) @@ -111,7 +110,7 @@ internal class SnapshotStateStackTest { Triple(listOf("item 1"), 0, true), Triple(listOf("item 1"), 1, false), Triple(listOf("item 1", "item 2"), 0, true), - Triple(listOf("item 1", "item 2"), 1, true) + Triple(listOf("item 1", "item 2"), 1, true), ).map { (items, minSize, expected) -> dynamicTest("when items size is ${items.size} then return $expected") { val stack = SnapshotStateStack(items, minSize) @@ -125,7 +124,7 @@ internal class SnapshotStateStackTest { sequenceOf( emptyList() to "new item", listOf("item 1") to "new item", - listOf("item 1", "item 2") to "new item" + listOf("item 1", "item 2") to "new item", ).map { (items, newItem) -> dynamicTest("when stack size is ${items.size} then last item is new item") { val stack = SnapshotStateStack(items) @@ -141,11 +140,11 @@ internal class SnapshotStateStackTest { sequenceOf( emptyList() to emptyList(), listOf("item 1") to listOf("new item"), - listOf("item 1", "item 2") to listOf("new item 1", "new item 2") + listOf("item 1", "item 2") to listOf("new item 1", "new item 2"), ).map { (items, newItems) -> dynamicTest( "when stack size is ${items.size} and new items size is ${newItems.size} " + - "then last item is new item" + "then last item is new item", ) { val stack = SnapshotStateStack(items) @@ -160,7 +159,7 @@ internal class SnapshotStateStackTest { sequenceOf( emptyList() to "new item", listOf("item 1") to "new item", - listOf("item 1", "item 2") to "new item" + listOf("item 1", "item 2") to "new item", ).map { (items, newItem) -> dynamicTest("when stack size is ${items.size} then last item is new item") { val stack = SnapshotStateStack(items) @@ -176,7 +175,7 @@ internal class SnapshotStateStackTest { sequenceOf( emptyList() to "new item", listOf("item 1") to "new item", - listOf("item 1", "item 2") to "new item" + listOf("item 1", "item 2") to "new item", ).map { (items, newItem) -> dynamicTest("when stack size is ${items.size} then last item is new item and size is one") { val stack = SnapshotStateStack(items) @@ -195,11 +194,11 @@ internal class SnapshotStateStackTest { Quadruple(listOf("item 1"), 0, true, null), Quadruple(listOf("item 1"), 1, false, "item 1"), Quadruple(listOf("item 1", "item 2"), 0, true, "item 1"), - Quadruple(listOf("item 1", "item 2"), 1, true, "item 1") + Quadruple(listOf("item 1", "item 2"), 1, true, "item 1"), ).map { (items, minSize, expected, lastItem) -> dynamicTest( "when stack size is ${items.size} and min size is $minSize " + - "then return $expected and last item is $lastItem" + "then return $expected and last item is $lastItem", ) { val stack = SnapshotStateStack(items, minSize) @@ -215,11 +214,11 @@ internal class SnapshotStateStackTest { Quadruple(listOf("item 1"), 0, 0, null), Quadruple(listOf("item 1"), 1, 1, "item 1"), Quadruple(listOf("item 1", "item 2"), 0, 0, null), - Quadruple(listOf("item 1", "item 2"), 1, 1, "item 1") + Quadruple(listOf("item 1", "item 2"), 1, 1, "item 1"), ).map { (items, minSize, expected, lastItem) -> dynamicTest( "when stack size is ${items.size} and min size is $minSize " + - "then size is $expected and last item is $lastItem" + "then size is $expected and last item is $lastItem", ) { val stack = SnapshotStateStack(items, minSize) @@ -239,11 +238,11 @@ internal class SnapshotStateStackTest { Quadruple(listOf("item 1", "item 2"), 0, "item 1", "item 1"), Quadruple(listOf("item 1", "item 2"), 1, "item 1", "item 1"), Quadruple(listOf("item 1", "item 2"), 0, "item 2", "item 2"), - Quadruple(listOf("item 1", "item 2"), 1, "item 2", "item 2") + Quadruple(listOf("item 1", "item 2"), 1, "item 2", "item 2"), ).map { (items, minSize, popItem, lastItem) -> dynamicTest( "when stack size is ${items.size}, min size is $minSize and pop until \"$popItem\" " + - "then last item is $lastItem" + "then last item is $lastItem", ) { val stack = SnapshotStateStack(items, minSize) diff --git a/voyager-core/src/commonJvmTest/kotlin/cafe/adriel/voyager/core/utils/Quadruple.kt b/voyager-core/src/commonJvmTest/kotlin/cafe/adriel/voyager/core/utils/Quadruple.kt index 21e0145c..ae30b0ce 100644 --- a/voyager-core/src/commonJvmTest/kotlin/cafe/adriel/voyager/core/utils/Quadruple.kt +++ b/voyager-core/src/commonJvmTest/kotlin/cafe/adriel/voyager/core/utils/Quadruple.kt @@ -4,5 +4,5 @@ internal data class Quadruple( val first: A, val second: B, val third: C, - val fourth: D + val fourth: D, ) diff --git a/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/annotation/InternalVoyagerApi.kt b/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/annotation/InternalVoyagerApi.kt index 71e9baba..2def6953 100644 --- a/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/annotation/InternalVoyagerApi.kt +++ b/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/annotation/InternalVoyagerApi.kt @@ -6,12 +6,12 @@ package cafe.adriel.voyager.core.annotation AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS, - AnnotationTarget.CONSTRUCTOR - ] + AnnotationTarget.CONSTRUCTOR, + ], ) @RequiresOptIn( level = RequiresOptIn.Level.ERROR, - message = "This API is Voyager Internal. It may be changed in the future without notice." + message = "This API is Voyager Internal. It may be changed in the future without notice.", ) public annotation class InternalVoyagerApi @@ -21,11 +21,11 @@ public annotation class InternalVoyagerApi AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS, - AnnotationTarget.CONSTRUCTOR - ] + AnnotationTarget.CONSTRUCTOR, + ], ) @RequiresOptIn( level = RequiresOptIn.Level.ERROR, - message = "This API is experimental. It may be changed in the future without notice." + message = "This API is experimental. It may be changed in the future without notice.", ) public annotation class ExperimentalVoyagerApi diff --git a/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/concurrent/ThreadSafeList.kt b/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/concurrent/ThreadSafeList.kt index f976ece5..8fab6eb8 100644 --- a/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/concurrent/ThreadSafeList.kt +++ b/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/concurrent/ThreadSafeList.kt @@ -2,24 +2,56 @@ package cafe.adriel.voyager.core.concurrent public expect class ThreadSafeList() : MutableList { override fun add(element: T): Boolean - override fun add(index: Int, element: T) + + override fun add( + index: Int, + element: T, + ) + override fun addAll(elements: Collection): Boolean - override fun addAll(index: Int, elements: Collection): Boolean + + override fun addAll( + index: Int, + elements: Collection, + ): Boolean + override fun clear() + override fun listIterator(): MutableListIterator + override fun listIterator(index: Int): MutableListIterator + override fun remove(element: T): Boolean + override fun removeAll(elements: Collection): Boolean + override fun removeAt(index: Int): T + override fun retainAll(elements: Collection): Boolean - override fun set(index: Int, element: T): T - override fun subList(fromIndex: Int, toIndex: Int): MutableList + + override fun set( + index: Int, + element: T, + ): T + + override fun subList( + fromIndex: Int, + toIndex: Int, + ): MutableList + override fun contains(element: T): Boolean + override fun containsAll(elements: Collection): Boolean + override fun get(index: Int): T + override fun indexOf(element: T): Int + override fun isEmpty(): Boolean + override fun iterator(): MutableIterator + override fun lastIndexOf(element: T): Int + override val size: Int } diff --git a/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/concurrent/ThreadSafeMap.kt b/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/concurrent/ThreadSafeMap.kt index c518858b..9ae6f4a1 100644 --- a/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/concurrent/ThreadSafeMap.kt +++ b/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/concurrent/ThreadSafeMap.kt @@ -2,15 +2,27 @@ package cafe.adriel.voyager.core.concurrent public expect class ThreadSafeMap() : MutableMap { override fun clear() - override fun put(key: K, value: V): V? + + override fun put( + key: K, + value: V, + ): V? + override fun putAll(from: Map) + override fun remove(key: K): V? + override val entries: MutableSet> override val keys: MutableSet override val values: MutableCollection + override fun containsKey(key: K): Boolean + override fun containsValue(value: V): Boolean + override fun get(key: K): V? + override fun isEmpty(): Boolean + override val size: Int } diff --git a/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/concurrent/ThreadSafeSet.kt b/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/concurrent/ThreadSafeSet.kt index 4178525a..92a0fb37 100644 --- a/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/concurrent/ThreadSafeSet.kt +++ b/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/concurrent/ThreadSafeSet.kt @@ -2,14 +2,24 @@ package cafe.adriel.voyager.core.concurrent public expect class ThreadSafeSet() : MutableSet { override fun add(element: T): Boolean + override fun addAll(elements: Collection): Boolean + override fun clear() + override fun iterator(): MutableIterator + override fun remove(element: T): Boolean + override fun removeAll(elements: Collection): Boolean + override fun retainAll(elements: Collection): Boolean + override fun contains(element: T): Boolean + override fun containsAll(elements: Collection): Boolean + override fun isEmpty(): Boolean + override val size: Int } diff --git a/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/internal/SafeCollections.kt b/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/internal/SafeCollections.kt index c40fc398..a7f817b9 100644 --- a/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/internal/SafeCollections.kt +++ b/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/internal/SafeCollections.kt @@ -7,8 +7,7 @@ package cafe.adriel.voyager.core.internal internal fun MutableList.removeFirstElement(): T = if (isEmpty()) throw NoSuchElementException("List is empty.") else removeAt(0) -internal fun MutableList.removeFirstElementOrNull(): T? = - if (isEmpty()) null else removeAt(0) +internal fun MutableList.removeFirstElementOrNull(): T? = if (isEmpty()) null else removeAt(0) internal fun MutableList.removeLastElement(): T = if (isEmpty()) throw NoSuchElementException("List is empty.") else removeAt(lastIndex) diff --git a/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/lifecycle/DisposableEffect.kt b/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/lifecycle/DisposableEffect.kt index 74f1bfc9..6d351b7f 100644 --- a/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/lifecycle/DisposableEffect.kt +++ b/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/lifecycle/DisposableEffect.kt @@ -11,7 +11,7 @@ import androidx.compose.runtime.remember @NonRestartableComposable public fun DisposableEffectIgnoringConfiguration( key1: Any?, - effect: DisposableEffectScope.() -> DisposableEffectResult + effect: DisposableEffectScope.() -> DisposableEffectResult, ) { val configurationChecker = getConfigurationChecker() remember(configurationChecker, key1) { DisposableEffectIgnoringConfigurationImpl(configurationChecker, effect) } @@ -22,7 +22,7 @@ public fun DisposableEffectIgnoringConfiguration( public fun DisposableEffectIgnoringConfiguration( key1: Any?, key2: Any?, - effect: DisposableEffectScope.() -> DisposableEffectResult + effect: DisposableEffectScope.() -> DisposableEffectResult, ) { val configurationChecker = getConfigurationChecker() remember(configurationChecker, key1, key2) { @@ -36,7 +36,7 @@ public fun DisposableEffectIgnoringConfiguration( key1: Any?, key2: Any?, key3: Any?, - effect: DisposableEffectScope.() -> DisposableEffectResult + effect: DisposableEffectScope.() -> DisposableEffectResult, ) { val configurationChecker = getConfigurationChecker() remember(configurationChecker, key1, key2, key3) { @@ -49,7 +49,7 @@ public fun DisposableEffectIgnoringConfiguration( @Suppress("ArrayReturn") public fun DisposableEffectIgnoringConfiguration( vararg keys: Any?, - effect: DisposableEffectScope.() -> DisposableEffectResult + effect: DisposableEffectScope.() -> DisposableEffectResult, ) { val configurationChecker = getConfigurationChecker() remember(configurationChecker, *keys) { DisposableEffectIgnoringConfigurationImpl(configurationChecker, effect) } @@ -59,7 +59,7 @@ private val InternalDisposableEffectScope = DisposableEffectScope() private class DisposableEffectIgnoringConfigurationImpl( private val configurationChecker: ConfigurationChecker, - private val effect: DisposableEffectScope.() -> DisposableEffectResult + private val effect: DisposableEffectScope.() -> DisposableEffectResult, ) : RememberObserver { private var onDispose: DisposableEffectResult? = null diff --git a/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/lifecycle/LifecycleEffectStore.kt b/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/lifecycle/LifecycleEffectStore.kt index 0435a89a..4ff6a57d 100644 --- a/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/lifecycle/LifecycleEffectStore.kt +++ b/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/lifecycle/LifecycleEffectStore.kt @@ -8,7 +8,10 @@ import cafe.adriel.voyager.core.screen.ScreenKey internal object LifecycleEffectStore : ScreenDisposable { private val executedLifecycles = ThreadSafeMap>() - fun store(screen: Screen, effectKey: String): LifecycleEffectOnceScope { + fun store( + screen: Screen, + effectKey: String, + ): LifecycleEffectOnceScope { val set = executedLifecycles.getOrPut(screen.key) { ThreadSafeSet() } val scope = LifecycleEffectOnceScope(uniqueKey = effectKey, set.size + 1) set.add(scope) @@ -16,8 +19,10 @@ internal object LifecycleEffectStore : ScreenDisposable { return scope } - fun hasExecuted(screen: Screen, effectKey: String): Boolean = - executedLifecycles.get(screen.key)?.any { it.uniqueKey == effectKey } == true + fun hasExecuted( + screen: Screen, + effectKey: String, + ): Boolean = executedLifecycles.get(screen.key)?.any { it.uniqueKey == effectKey } == true override fun onDispose(screen: Screen) { val scopes = executedLifecycles.remove(screen.key) diff --git a/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/lifecycle/multipleScreenLifecycleOwnerUtil.kt b/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/lifecycle/MultipleScreenLifecycleOwnerUtil.kt similarity index 90% rename from voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/lifecycle/multipleScreenLifecycleOwnerUtil.kt rename to voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/lifecycle/MultipleScreenLifecycleOwnerUtil.kt index e14e240b..b8e9b841 100644 --- a/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/lifecycle/multipleScreenLifecycleOwnerUtil.kt +++ b/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/lifecycle/MultipleScreenLifecycleOwnerUtil.kt @@ -11,7 +11,7 @@ import cafe.adriel.voyager.core.internal.removeFirstElementOrNull public fun MultipleProvideBeforeScreenContent( screenLifecycleContentProviders: List, provideSaveableState: @Composable (suffixKey: String, content: @Composable () -> Unit) -> Unit, - content: @Composable () -> Unit + content: @Composable () -> Unit, ) { if (screenLifecycleContentProviders.isNotEmpty()) { val copy = screenLifecycleContentProviders.toMutableList() @@ -19,7 +19,7 @@ public fun MultipleProvideBeforeScreenContent( screenLifecycleContentProvider = copy.removeFirstElement(), provideSaveableState = provideSaveableState, content = content, - nextOrNull = { copy.removeFirstElementOrNull() } + nextOrNull = { copy.removeFirstElementOrNull() }, ) } else { content() @@ -31,7 +31,7 @@ private fun RecursiveProvideBeforeScreenContent( screenLifecycleContentProvider: ScreenLifecycleContentProvider, provideSaveableState: @Composable (suffixKey: String, content: @Composable () -> Unit) -> Unit, content: @Composable () -> Unit, - nextOrNull: () -> ScreenLifecycleContentProvider? + nextOrNull: () -> ScreenLifecycleContentProvider?, ) { val next = remember(screenLifecycleContentProvider, provideSaveableState, content, nextOrNull) { nextOrNull() } if (next != null) { @@ -40,13 +40,13 @@ private fun RecursiveProvideBeforeScreenContent( screenLifecycleContentProvider = next, provideSaveableState = provideSaveableState, content = content, - nextOrNull = nextOrNull + nextOrNull = nextOrNull, ) } screenLifecycleContentProvider.ProvideBeforeScreenContent( provideSaveableState = { suffixKey, _ -> provideSaveableState(suffixKey, recursiveContent) - } + }, ) { recursiveContent() } @@ -54,7 +54,7 @@ private fun RecursiveProvideBeforeScreenContent( screenLifecycleContentProvider.ProvideBeforeScreenContent( provideSaveableState = { suffixKey, content -> provideSaveableState(suffixKey, content) - } + }, ) { content() } diff --git a/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/lifecycle/NavigatorScreenLifecycle.kt b/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/lifecycle/NavigatorScreenLifecycle.kt index 816ce844..59f2d752 100644 --- a/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/lifecycle/NavigatorScreenLifecycle.kt +++ b/voyager-core/src/commonMain/kotlin/cafe/adriel/voyager/core/lifecycle/NavigatorScreenLifecycle.kt @@ -11,6 +11,5 @@ public val LocalNavigatorScreenLifecycleProvider: ProvidableCompositionLocal