Skip to content

Commit

Permalink
Add support to save non-mutable int in StateSnapshot (#1508)
Browse files Browse the repository at this point in the history
* Add support to save non-mutable int in StateSnapshot

* clean up & add tests

* comments
  • Loading branch information
jingwei99 authored Sep 26, 2023
1 parent 052acf1 commit 52fcbda
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ private class RedwoodZiplineTreehouseUi(
)
}

override fun snapshotState(): StateSnapshot? {
override fun snapshotState(): StateSnapshot {
val savedState = saveableStateRegistry.performSave()
return savedState.toStateSnapshot()
}
Expand Down
7 changes: 7 additions & 0 deletions redwood-treehouse/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ kotlin {
api libs.zipline
}
}

commonTest {
dependencies {
implementation libs.kotlin.test
implementation libs.assertk
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,28 @@ package app.cash.redwood.treehouse
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import kotlin.jvm.JvmInline
import kotlinx.serialization.Contextual
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.booleanOrNull
import kotlinx.serialization.json.doubleOrNull
import kotlinx.serialization.json.intOrNull

@Serializable
public class StateSnapshot(
public val content: Map<String, List<JsonElement?>>,
public val content: Map<String, List<@Contextual Saveable>>,
) {
public fun toValuesMap(): Map<String, List<Any?>>? {
public fun toValuesMap(): Map<String, List<Any?>> {
return content.mapValues { entry ->
entry.value.map { mutableStateOf(it.fromJsonElement()) }
entry.value.map {
if (it.isMutableState) {
mutableStateOf(it.value)
} else {
it.value.fromJsonElement()
}
}
}
}

Expand All @@ -48,8 +56,8 @@ public fun Map<String, List<Any?>>.toStateSnapshot(): StateSnapshot = StateSnaps
mapValues { entry ->
entry.value.map { element ->
when (element) {
is MutableState<*> -> element.value.toJsonElement()
else -> error("unexpected type: $this")
is MutableState<*> -> Saveable(true, element.value.toJsonElement())
else -> Saveable(false, element.toJsonElement())
}
}
},
Expand All @@ -58,6 +66,7 @@ public fun Map<String, List<Any?>>.toStateSnapshot(): StateSnapshot = StateSnaps
private fun Any?.toJsonElement(): JsonElement {
return when (this) {
is String -> JsonPrimitive(this)
is Int -> JsonPrimitive(this)
is List<*> -> JsonArray(map { it.toJsonElement() })
is JsonElement -> this
else -> error("unexpected type: $this")
Expand All @@ -68,11 +77,9 @@ private fun Any?.toJsonElement(): JsonElement {
private fun JsonElement?.fromJsonElement(): Any {
return when (this) {
is JsonPrimitive -> {
if (this.isString) {
return content
}
return booleanOrNull ?: doubleOrNull ?: error("unexpected type: $this")
// TODO add other primitive types (double, float, long) when needed
if (this.isString) return content
return booleanOrNull ?: doubleOrNull ?: intOrNull ?: error("unexpected type: $this")
// TODO add other primitive types (float, long) when needed
}

is JsonArray -> listOf({ this.forEach { it.toJsonElement() } })
Expand All @@ -81,3 +88,8 @@ private fun JsonElement?.fromJsonElement(): Any {
else -> error("unexpected type: $this")
}
}

public data class Saveable(
val isMutableState: Boolean,
val value: JsonElement,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* 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.containsOnly
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).containsOnly(
"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 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"),
)
}

0 comments on commit 52fcbda

Please sign in to comment.