Skip to content

Commit

Permalink
Resources. Don't return a cached value when pass new args.
Browse files Browse the repository at this point in the history
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 #4325
  • Loading branch information
igordmn committed Feb 19, 2024
1 parent 55cc437 commit 4e948e4
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,43 @@ import kotlinx.coroutines.runBlocking

@Composable
internal actual fun <T> rememberResourceState(
key: Any,
key1: Any,
getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T
): State<T> {
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(
runBlocking { block(environment) }
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,34 @@ import androidx.compose.runtime.State
*/
@Composable
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,
block: suspend (ResourceEnvironment) -> T
): State<T>
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ private suspend fun loadString(
fun stringResource(resource: StringResource, vararg formatArgs: Any): String {
val resourceReader = LocalResourceReader.current
val args = formatArgs.map { it.toString() }
val str by rememberResourceState(resource, { "" }) { env ->
val str by rememberResourceState(resource, args, { "" }) { env ->
loadString(resource, args, resourceReader, env)
}
return str
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,29 @@ class ComposeResourceTest {
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
fun testLoadStringResource() = runTest {
assertEquals("Compose Resources App", getString(TestStringResource("app_name")))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ private val defaultEmptyFont by lazy { Font("org.jetbrains.compose.emptyFont", B
@Composable
actual fun Font(resource: FontResource, weight: FontWeight, style: FontStyle): Font {
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 fontBytes = resourceReader.read(path)
Font(path, fontBytes, weight, style)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,44 @@ import androidx.compose.runtime.remember

@Composable
internal actual fun <T> rememberResourceState(
key: Any,
key1: Any,
getDefault: () -> T,
block: suspend (ResourceEnvironment) -> T
): State<T> {
val environment = LocalComposeEnvironment.current.rememberEnvironment()
val state = remember(key) { mutableStateOf(getDefault()) }
LaunchedEffect(key) {
val state = remember(key1) { mutableStateOf(getDefault()) }
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)
}
return state
Expand Down

0 comments on commit 4e948e4

Please sign in to comment.