diff --git a/demo-android/src/main/kotlin/com/svenjacobs/reveal/demo/ui/MainScreen.kt b/demo-android/src/main/kotlin/com/svenjacobs/reveal/demo/ui/MainScreen.kt index ae9ba15..42262a3 100644 --- a/demo-android/src/main/kotlin/com/svenjacobs/reveal/demo/ui/MainScreen.kt +++ b/demo-android/src/main/kotlin/com/svenjacobs/reveal/demo/ui/MainScreen.kt @@ -41,6 +41,7 @@ fun MainScreen(modifier: Modifier = Modifier) { val revealState = rememberRevealState() LaunchedEffect(Unit) { + if (revealState.isVisible) return@LaunchedEffect delay(2.seconds) revealState.reveal(Keys.Fab) } diff --git a/reveal-core/src/main/kotlin/com/svenjacobs/reveal/RevealState.kt b/reveal-core/src/main/kotlin/com/svenjacobs/reveal/RevealState.kt index 264df75..2fd7f29 100644 --- a/reveal-core/src/main/kotlin/com/svenjacobs/reveal/RevealState.kt +++ b/reveal-core/src/main/kotlin/com/svenjacobs/reveal/RevealState.kt @@ -4,26 +4,39 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.Saver +import androidx.compose.runtime.saveable.autoSaver +import androidx.compose.runtime.saveable.listSaver +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import com.svenjacobs.reveal.internal.revealable.Revealable import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @Stable -public class RevealState { +public class RevealState internal constructor( + visible: Boolean = false, + private val restoreRevealableKey: Key? = null, +) { private val mutex = Mutex() + private var didRestoreCurrentRevealable = false - internal var visible by mutableStateOf(false) + internal var visible by mutableStateOf(visible) private set internal var currentRevealable by mutableStateOf(null) private set private val revealables: MutableMap = mutableMapOf() + public val isVisible: Boolean + get() = visible + + public val currentRevealableKey: Key? + get() = currentRevealable?.key + public suspend fun reveal(key: Key) { + require(revealables.containsKey(key)) { "Revealable with key \"$key\" not found" } mutex.withLock { - // TODO: hide when key was not found? currentRevealable = revealables[key] visible = true } @@ -31,14 +44,47 @@ public class RevealState { public suspend fun hide() { mutex.withLock { + currentRevealable = null visible = false } } internal fun putRevealable(revealable: Revealable) { revealables[revealable.key] = revealable + + if (!didRestoreCurrentRevealable && restoreRevealableKey == revealable.key) { + currentRevealable = revealable + didRestoreCurrentRevealable = true + } + } + + internal companion object { + + internal fun newSaver(keySaver: Saver): Saver = listSaver( + save = { + listOf( + it.isVisible, + it.currentRevealableKey?.let { key -> with(keySaver) { save(key) } }, + ) + }, + restore = { + RevealState( + visible = it[0] as Boolean, + restoreRevealableKey = it[1]?.let { keySaveable -> keySaver.restore(keySaveable) }, + ) + }, + ) } } +/** + * Creates a [RevealState] that is remembered across compositions. + * + * If a custom type is used for revealable keys which cannot be saved automatically by Compose, + * a custom saver must be specified via [keySaver]. + * + * @param keySaver Custom saver for revealable keys + */ @Composable -public fun rememberRevealState(): RevealState = remember { RevealState() } +public fun rememberRevealState(keySaver: Saver = autoSaver()): RevealState = + rememberSaveable(saver = RevealState.newSaver(keySaver)) { RevealState() }