JetBrains / compose-multiplatform

Compose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.
https://jetbrains.com/lp/compose-multiplatform
Apache License 2.0
16.15k stars 1.17k forks source link

Crash on js: An operation is not implemented: implement native toLanguageTag #2815

Closed jeran closed 1 year ago

jeran commented 1 year ago

Describe the bug

Using compose-jb on js can cause an "An operation is not implemented" crash in certain scenarios. Related to https://github.com/JetBrains/compose-jb/issues/2773 but this is ticket is for js. I'm not entirely sure in what scenario this is triggered, but I believe it requires using a TextField and swapping out the theme's colors (for us it occurs when switching to dark mode).

Affected platforms

Versions

To Reproduce Steps and/or the code snippet to reproduce the behavior: I haven't isolated this in a standalone project, but the source code shows it's just an unimplemented method.

Expected behavior The application should not crash

Additional context In JsPlatformLocale, the method is not implemented. See: JsPlatformLocale.js.kt#L29 JsPlatformLocale.js.kt#L29

dima-avdeev-jb commented 1 year ago

@eymar Recently i fixed the same for iOS. https://github.com/JetBrains/androidx/pull/408/files

The easiest workaround is always return "en-US"

dimitree54 commented 1 year ago

Found a minimal code reproducing an error (for JS, works on other).

// main.js.kt
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.window.Window
import org.jetbrains.skiko.wasm.onWasmReady

fun main() {
    onWasmReady {
        Window("DelmeApp") {
            Column(modifier = Modifier.fillMaxSize()) {
                val enabled = remember { mutableStateOf(true) }
                TextField("Hello", {}, enabled = enabled.value)
                Button( {
                    enabled.value = !enabled.value
                }){
                    Text("Toggle")
                }
            }
        }
    }
}

When you press the button, error appears:

NotImplementedError
cause: undefined
message: "An operation is not implemented: implement native toLanguageTag"
name: "NotImplementedError"
stack: "captureStack@↵NotImplementedError@↵@↵@↵@↵@↵@↵@↵@↵@↵@↵@↵@↵@↵materialize@↵@↵@↵SimpleLayout$composable@↵@↵@↵invoke$invoke@↵@↵…"

@dima-avdeev-jb could tell more about workaround, where I should return "en-US" from?

dima-avdeev-jb commented 1 year ago

@dimitree54 This workaround should be applied on our side.

gleb-skobinsky commented 1 year ago

I'm experiencing the same issue with JS target when trying to switch from light to dark theme. Where is the right place to provide locale? The error still persists in 1.4.0 Compose version

gleb-skobinsky commented 1 year ago

This is especially frustrating since switching UI themes seems to have nothing to do with locales...

eygraber commented 1 year ago

I'm experiencing the same as @gleb-skobinsky with 1.5.0-dev1043

jeran commented 1 year ago

for those who need a very short term solution, we ended up editing the js directly after it's built via our gradle build script.

in our case we are publishing our js module as a library to github using the npm-publish plugin, so right before that task we swap out the NotImplementedError with an empty string return.

not much of a gradle smith, so there's almost definitely a better way to do this

tasks.getByName("publishJsPackageToGithubRegistry").doFirst {
    val error =
        "throw new NotImplementedError('An operation is not implemented: implement native toLanguageTag');"
    val implementation = "return \"\";"
    val file = file("build/packages/js/androidx-ui-text.js")
    val buffer = StringBuffer()
    file.forEachLine { line ->
        buffer.appendLine(line.replace(error, implementation))
    }
    file.writeText(buffer.toString())
}
gleb-skobinsky commented 1 year ago

Thanks! A similar workaround helped me to override the error for the browser app. It looks too ugly to be posted here, but does the trick at least for now

jeran commented 1 year ago

I believe this is fixed, but I'll let ya'll close it out.

dima-avdeev-jb commented 1 year ago

Done, fixed here: https://github.com/JetBrains/compose-multiplatform-core/blob/e92f3b2860e19da41f2ef615d5c0646f413d7b32/compose/ui/ui-text/src/jsWasmMain/kotlin/androidx/compose/ui/text/intl/JsPlatformLocale.js.kt#L32

okushnikov commented 3 months ago

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.