Browse Source

Resources. Don't return a cached value when pass new args.

The issue was because we cache the value in the current composition, and the next composition returns the cached value.

There weren't an issue if we just call one `stringResource` after another because those are different compositions.

Fixes https://github.com/JetBrains/compose-multiplatform/issues/4325
igor.demin/fix-pom-publication v1.6.0
Igor Demin 9 months ago
parent
commit
4e948e4bc3
  1. 35
      components/resources/library/src/blockingMain/kotlin/org/jetbrains/compose/resources/ResourceState.blocking.kt
  2. 29
      components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/ResourceState.kt
  3. 2
      components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/StringResources.kt
  4. 23
      components/resources/library/src/commonTest/kotlin/org/jetbrains/compose/resources/ComposeResourceTest.kt
  5. 2
      components/resources/library/src/skikoMain/kotlin/org/jetbrains/compose/resources/FontResources.skiko.kt
  6. 37
      components/resources/library/src/webMain/kotlin/org/jetbrains/compose/resources/ResourceState.web.kt

35
components/resources/library/src/blockingMain/kotlin/org/jetbrains/compose/resources/ResourceState.blocking.kt

@ -5,12 +5,43 @@ import kotlinx.coroutines.runBlocking
@Composable @Composable
internal actual fun <T> rememberResourceState( internal actual fun <T> rememberResourceState(
key: Any, key1: Any,
getDefault: () -> T, getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T block: suspend (ResourceEnvironment) -> T
): State<T> { ): State<T> {
val environment = LocalComposeEnvironment.current.rememberEnvironment() val environment = LocalComposeEnvironment.current.rememberEnvironment()
return remember(key, environment) { return remember(key1, environment) {
mutableStateOf(
runBlocking { block(environment) }
)
}
}
@Composable
internal actual fun <T> rememberResourceState(
key1: Any,
key2: Any,
getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T
): State<T> {
val environment = LocalComposeEnvironment.current.rememberEnvironment()
return remember(key1, key2, environment) {
mutableStateOf(
runBlocking { block(environment) }
)
}
}
@Composable
internal actual fun <T> rememberResourceState(
key1: Any,
key2: Any,
key3: Any,
getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T
): State<T> {
val environment = LocalComposeEnvironment.current.rememberEnvironment()
return remember(key1, key2, key3, environment) {
mutableStateOf( mutableStateOf(
runBlocking { block(environment) } runBlocking { block(environment) }
) )

29
components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/ResourceState.kt

@ -10,7 +10,34 @@ import androidx.compose.runtime.State
*/ */
@Composable @Composable
internal expect fun <T> rememberResourceState( internal expect fun <T> rememberResourceState(
key: Any, key1: Any,
getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T
): State<T>
/**
* This is a platform-specific function that calculates and remembers a state.
* For all platforms except a JS it is a blocking function.
* On the JS platform it loads the state asynchronously and uses `getDefault` as an initial state value.
*/
@Composable
internal expect fun <T> rememberResourceState(
key1: Any,
key2: Any,
getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T
): State<T>
/**
* This is a platform-specific function that calculates and remembers a state.
* For all platforms except a JS it is a blocking function.
* On the JS platform it loads the state asynchronously and uses `getDefault` as an initial state value.
*/
@Composable
internal expect fun <T> rememberResourceState(
key1: Any,
key2: Any,
key3: Any,
getDefault: () -> T, getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T block: suspend (ResourceEnvironment) -> T
): State<T> ): State<T>

2
components/resources/library/src/commonMain/kotlin/org/jetbrains/compose/resources/StringResources.kt

@ -123,7 +123,7 @@ private suspend fun loadString(
fun stringResource(resource: StringResource, vararg formatArgs: Any): String { fun stringResource(resource: StringResource, vararg formatArgs: Any): String {
val resourceReader = LocalResourceReader.current val resourceReader = LocalResourceReader.current
val args = formatArgs.map { it.toString() } val args = formatArgs.map { it.toString() }
val str by rememberResourceState(resource, { "" }) { env -> val str by rememberResourceState(resource, args, { "" }) { env ->
loadString(resource, args, resourceReader, env) loadString(resource, args, resourceReader, env)
} }
return str return str

23
components/resources/library/src/commonTest/kotlin/org/jetbrains/compose/resources/ComposeResourceTest.kt

@ -111,6 +111,29 @@ class ComposeResourceTest {
assertEquals(listOf("item 1", "item 2", "item 3"), str_arr) assertEquals(listOf("item 1", "item 2", "item 3"), str_arr)
} }
// https://github.com/JetBrains/compose-multiplatform/issues/4325
@Test
fun testReadStringFromDifferentArgs() = runComposeUiTest {
var arg by mutableStateOf(42)
var str1 = ""
var str2 = ""
setContent {
CompositionLocalProvider(LocalComposeEnvironment provides TestComposeEnvironment) {
str1 = stringResource(TestStringResource("str_template"), "test1", arg)
str2 = stringResource(TestStringResource("str_template"), "test2", arg)
}
}
waitForIdle()
assertEquals("Hello, test1! You have 42 new messages.", str1)
assertEquals("Hello, test2! You have 42 new messages.", str2)
arg = 31415
waitForIdle()
assertEquals("Hello, test1! You have 31415 new messages.", str1)
assertEquals("Hello, test2! You have 31415 new messages.", str2)
}
@Test @Test
fun testLoadStringResource() = runTest { fun testLoadStringResource() = runTest {
assertEquals("Compose Resources App", getString(TestStringResource("app_name"))) assertEquals("Compose Resources App", getString(TestStringResource("app_name")))

2
components/resources/library/src/skikoMain/kotlin/org/jetbrains/compose/resources/FontResources.skiko.kt

@ -33,7 +33,7 @@ private val defaultEmptyFont by lazy { Font("org.jetbrains.compose.emptyFont", B
@Composable @Composable
actual fun Font(resource: FontResource, weight: FontWeight, style: FontStyle): Font { actual fun Font(resource: FontResource, weight: FontWeight, style: FontStyle): Font {
val resourceReader = LocalResourceReader.current val resourceReader = LocalResourceReader.current
val fontFile by rememberResourceState(resource, { defaultEmptyFont }) { env -> val fontFile by rememberResourceState(resource, weight, style, { defaultEmptyFont }) { env ->
val path = resource.getPathByEnvironment(env) val path = resource.getPathByEnvironment(env)
val fontBytes = resourceReader.read(path) val fontBytes = resourceReader.read(path)
Font(path, fontBytes, weight, style) Font(path, fontBytes, weight, style)

37
components/resources/library/src/webMain/kotlin/org/jetbrains/compose/resources/ResourceState.web.kt

@ -8,13 +8,44 @@ import androidx.compose.runtime.remember
@Composable @Composable
internal actual fun <T> rememberResourceState( internal actual fun <T> rememberResourceState(
key: Any, key1: Any,
getDefault: () -> T, getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T block: suspend (ResourceEnvironment) -> T
): State<T> { ): State<T> {
val environment = LocalComposeEnvironment.current.rememberEnvironment() val environment = LocalComposeEnvironment.current.rememberEnvironment()
val state = remember(key) { mutableStateOf(getDefault()) } val state = remember(key1) { mutableStateOf(getDefault()) }
LaunchedEffect(key) { LaunchedEffect(key1) {
state.value = block(environment)
}
return state
}
@Composable
internal actual fun <T> rememberResourceState(
key1: Any,
key2: Any,
getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T
): State<T> {
val environment = LocalComposeEnvironment.current.rememberEnvironment()
val state = remember(key1, key2) { mutableStateOf(getDefault()) }
LaunchedEffect(key1, key2) {
state.value = block(environment)
}
return state
}
@Composable
internal actual fun <T> rememberResourceState(
key1: Any,
key2: Any,
key3: Any,
getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T
): State<T> {
val environment = LocalComposeEnvironment.current.rememberEnvironment()
val state = remember(key1, key2, key3) { mutableStateOf(getDefault()) }
LaunchedEffect(key1, key2, key3) {
state.value = block(environment) state.value = block(environment)
} }
return state return state

Loading…
Cancel
Save