Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lifecycle Effect Once API #425

Merged
merged 1 commit into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.lifecycle.LifecycleEffect
import cafe.adriel.voyager.core.lifecycle.LifecycleEffectOnce
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.core.screen.uniqueScreenKey
import cafe.adriel.voyager.navigator.LocalNavigator
Expand All @@ -36,6 +37,9 @@ data class BasicNavigationScreen(
onStarted = { Log.d("Navigator", "Start screen #$index") },
onDisposed = { Log.d("Navigator", "Dispose screen #$index") }
)
LifecycleEffectOnce {
Log.d("Navigator", "On screen first appear #$index")
}

val navigator = LocalNavigator.currentOrThrow

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package cafe.adriel.voyager.core.lifecycle

import cafe.adriel.voyager.core.concurrent.ThreadSafeMap
import cafe.adriel.voyager.core.concurrent.ThreadSafeSet
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.core.screen.ScreenKey

internal object LifecycleEffectStore : ScreenDisposable {
private val executedLifecycles = ThreadSafeMap<ScreenKey, ThreadSafeSet<String>>()

fun store(screen: Screen, effectKey: String) {
val set = executedLifecycles.getOrPut(screen.key) { ThreadSafeSet() }
set.add(effectKey)
}

fun hasExecuted(screen: Screen, effectKey: String): Boolean =
executedLifecycles.get(screen.key)?.contains(effectKey) == true

override fun onDispose(screen: Screen) {
executedLifecycles.remove(screen.key)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@ package cafe.adriel.voyager.core.lifecycle

import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import cafe.adriel.voyager.core.annotation.ExperimentalVoyagerApi
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.core.screen.randomUuid

@Deprecated(
message = "This API is a wrap on top on DisposableEffect, will be removed in 1.1.0, replace with DisposableEffect"
)
@Composable
public fun Screen.LifecycleEffect(
onStarted: () -> Unit = {},
Expand All @@ -16,6 +23,23 @@ public fun Screen.LifecycleEffect(
}
}

@ExperimentalVoyagerApi
@Composable
public fun Screen.LifecycleEffectOnce(onFirstAppear: () -> Unit) {
Copy link
Contributor

@shpasha shpasha May 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HelloI think it would be useful to have onDisappear callback here that is called when the screen disappears from the stack

val uniqueCompositionKey = rememberSaveable { randomUuid() }

val lifecycleEffectStore = remember {
ScreenLifecycleStore.get(this) { LifecycleEffectStore }
}

LaunchedEffect(Unit) {
if (lifecycleEffectStore.hasExecuted(this@LifecycleEffectOnce, uniqueCompositionKey).not()) {
lifecycleEffectStore.store(this@LifecycleEffectOnce, uniqueCompositionKey)
onFirstAppear()
}
}
}

@Composable
public fun rememberScreenLifecycleOwner(
screen: Screen
Expand Down
Loading