diff --git a/redwood-lazylayout-compose/build.gradle b/redwood-lazylayout-compose/build.gradle index c387c3bb72..0ed6f7fbf5 100644 --- a/redwood-lazylayout-compose/build.gradle +++ b/redwood-lazylayout-compose/build.gradle @@ -14,7 +14,6 @@ kotlin { commonMain { dependencies { api projects.redwoodLazylayoutWidget - implementation libs.jetbrains.compose.runtime.saveable } } commonTest { diff --git a/redwood-lazylayout-compose/src/commonMain/kotlin/app/cash/redwood/lazylayout/compose/LazyListState.kt b/redwood-lazylayout-compose/src/commonMain/kotlin/app/cash/redwood/lazylayout/compose/LazyListState.kt index 0225e9dbef..5975f27c58 100644 --- a/redwood-lazylayout-compose/src/commonMain/kotlin/app/cash/redwood/lazylayout/compose/LazyListState.kt +++ b/redwood-lazylayout-compose/src/commonMain/kotlin/app/cash/redwood/lazylayout/compose/LazyListState.kt @@ -15,18 +15,17 @@ */ package app.cash.redwood.lazylayout.compose -import androidx.compose.runtime.saveable.Saver -import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @Composable public fun rememberLazyListState( initialFirstVisibleItemIndex: Int = 0, ): LazyListState { - return rememberSaveable(saver = LazyListState.Saver) { + return remember { LazyListState( initialFirstVisibleItemIndex, ) @@ -47,17 +46,4 @@ public class LazyListState( firstVisibleItemIndex = index scrollToItemTriggeredId++ } - public companion object { - /** - * The default [Saver] implementation for [LazyListState]. - */ - public val Saver: Saver = Saver( - save = { it.firstVisibleItemIndex }, - restore = { - LazyListState( - firstVisibleItemIndex = it, - ) - } - ) - } } diff --git a/redwood-treehouse-guest/src/commonMain/kotlin/app/cash/redwood/treehouse/treehouseCompose.kt b/redwood-treehouse-guest/src/commonMain/kotlin/app/cash/redwood/treehouse/treehouseCompose.kt index 9d4ffda802..292ca50101 100644 --- a/redwood-treehouse-guest/src/commonMain/kotlin/app/cash/redwood/treehouse/treehouseCompose.kt +++ b/redwood-treehouse-guest/src/commonMain/kotlin/app/cash/redwood/treehouse/treehouseCompose.kt @@ -109,8 +109,7 @@ private class RedwoodZiplineTreehouseUi( ) } - override fun snapshotState(): StateSnapshot? { - // performSave is not picking up other values, why? + override fun snapshotState(): StateSnapshot { val savedState = saveableStateRegistry.performSave() return savedState.toStateSnapshot() } diff --git a/redwood-treehouse/build.gradle b/redwood-treehouse/build.gradle index b4b1f8bd43..5cb5e62f7a 100644 --- a/redwood-treehouse/build.gradle +++ b/redwood-treehouse/build.gradle @@ -21,6 +21,13 @@ kotlin { api libs.zipline } } + + commonTest { + dependencies { + implementation libs.kotlin.test + implementation libs.assertk + } + } } } diff --git a/redwood-treehouse/src/commonMain/kotlin/app/cash/redwood/treehouse/StateSnapshot.kt b/redwood-treehouse/src/commonMain/kotlin/app/cash/redwood/treehouse/StateSnapshot.kt index 1c8cffa072..cf6a278065 100644 --- a/redwood-treehouse/src/commonMain/kotlin/app/cash/redwood/treehouse/StateSnapshot.kt +++ b/redwood-treehouse/src/commonMain/kotlin/app/cash/redwood/treehouse/StateSnapshot.kt @@ -29,7 +29,7 @@ import kotlinx.serialization.json.intOrNull @Serializable public class StateSnapshot( - public val content: Map> + public val content: Map>, ) { public fun toValuesMap(): Map> { return content.mapValues { entry -> @@ -54,8 +54,8 @@ public class StateSnapshot( */ public fun Map>.toStateSnapshot(): StateSnapshot = StateSnapshot( mapValues { entry -> - entry.value.map {element -> - when(element) { + entry.value.map { element -> + when (element) { is MutableState<*> -> Saveable(true, element.value.toJsonElement()) else -> Saveable(false, element.toJsonElement()) } @@ -89,7 +89,7 @@ private fun JsonElement?.fromJsonElement(): Any { } } -public data class Saveable ( +public data class Saveable( val isMutableState: Boolean, - val value: JsonElement + val value: JsonElement, ) diff --git a/redwood-treehouse/src/commonTest/kotlin/app/cash/redwood/treehouse/StateSnapshotTest.kt b/redwood-treehouse/src/commonTest/kotlin/app/cash/redwood/treehouse/StateSnapshotTest.kt new file mode 100644 index 0000000000..f8efdeb852 --- /dev/null +++ b/redwood-treehouse/src/commonTest/kotlin/app/cash/redwood/treehouse/StateSnapshotTest.kt @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2023 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package app.cash.redwood.treehouse + +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import assertk.assertThat +import assertk.assertions.isEqualTo +import kotlin.test.Test +import kotlin.test.assertTrue +import kotlinx.serialization.json.JsonPrimitive + +class StateSnapshotTest { + + @Test + fun toValueMapWorksAsExpected() { + val stateSnapshot = stateSnapshot() + val valuesMap = stateSnapshot.toValuesMap() + assertThat(valuesMap.entries.size).isEqualTo(4) + assertTrue(valuesMap["key1"]!![0] is MutableState<*>) + assertThat((valuesMap["key1"]!![0] as MutableState<*>).value).isEqualTo(JsonPrimitive(1)) + + assertThat(valuesMap["key2"]).isEqualTo(listOf(1.0)) + + assertThat(valuesMap["key3"]!![0] is MutableState<*>) + assertThat((valuesMap["key3"]!![0] as MutableState<*>).value).isEqualTo(JsonPrimitive("str")) + + assertThat(valuesMap["key4"]).isEqualTo(listOf("str")) + } + + @Test + fun toStateSnapshotWorksAsExpected() { + val storedStateSnapshot = storedStateSnapshot() + val stateSnapshot = storedStateSnapshot.toStateSnapshot() + assertThat(stateSnapshot.content["key1"]!![0]).isEqualTo(Saveable(true, JsonPrimitive(1))) + assertThat(stateSnapshot.content["key2"]!![0]).isEqualTo(Saveable(false, JsonPrimitive(1))) + assertThat(stateSnapshot.content["key3"]!![0]).isEqualTo(Saveable(true, JsonPrimitive("str"))) + assertThat(stateSnapshot.content["key4"]!![0]).isEqualTo(Saveable(false, JsonPrimitive("str"))) + } + private fun stateSnapshot() = StateSnapshot( + mapOf( + "key1" to listOf(Saveable(true, JsonPrimitive(1))), + "key2" to listOf(Saveable(false, JsonPrimitive(1))), + "key3" to listOf(Saveable(true, JsonPrimitive("str"))), + "key4" to listOf(Saveable(false, JsonPrimitive("str"))), + ), + ) + + private fun storedStateSnapshot() = mapOf( + "key1" to listOf(mutableStateOf(1)), + "key2" to listOf(1), + "key3" to listOf(mutableStateOf("str")), + "key4" to listOf("str"), + ) +} diff --git a/samples/emoji-search/presenter/src/commonMain/kotlin/com/example/redwood/emojisearch/presenter/EmojiSearch.kt b/samples/emoji-search/presenter/src/commonMain/kotlin/com/example/redwood/emojisearch/presenter/EmojiSearch.kt index caab15a775..2406e2800c 100644 --- a/samples/emoji-search/presenter/src/commonMain/kotlin/com/example/redwood/emojisearch/presenter/EmojiSearch.kt +++ b/samples/emoji-search/presenter/src/commonMain/kotlin/com/example/redwood/emojisearch/presenter/EmojiSearch.kt @@ -36,7 +36,6 @@ import app.cash.redwood.layout.compose.Column import app.cash.redwood.layout.compose.Row import app.cash.redwood.lazylayout.compose.ExperimentalRedwoodLazyLayoutApi import app.cash.redwood.lazylayout.compose.LazyColumn -import app.cash.redwood.lazylayout.compose.LazyListState import app.cash.redwood.lazylayout.compose.items import app.cash.redwood.lazylayout.compose.rememberLazyListState import app.cash.redwood.ui.Margin @@ -97,9 +96,7 @@ private fun LazyColumn( override fun SaverScope.save(value: TextFieldState) = value.text } - var searchTerm by rememberSaveable(stateSaver = searchTermSaver) { - mutableStateOf(TextFieldState("")) - } + var searchTerm by rememberSaveable(stateSaver = searchTermSaver) { mutableStateOf(TextFieldState("")) } LaunchedEffect(refreshSignal) { try { @@ -176,14 +173,7 @@ private fun NestedFlexBoxContainers(httpClient: HttpClient, navigator: Navigator override fun SaverScope.save(value: TextFieldState) = value.text } - var searchTerm by rememberSaveable(stateSaver = searchTermSaver) { - error("boom") - mutableStateOf(TextFieldState("")) - } - var searchTerm1 by rememberSaveable(stateSaver = searchTermSaver) { - error("boom") - mutableStateOf(TextFieldState("hhh")) - } + var searchTerm by rememberSaveable(stateSaver = searchTermSaver) { mutableStateOf(TextFieldState("")) } LaunchedEffect(Unit) { try {