package dev.inmo.postssystem.features.auth.client import androidx.compose.runtime.mutableStateOf import dev.inmo.jsuikit.elements.* import dev.inmo.jsuikit.modifiers.* import dev.inmo.jsuikit.utils.Attrs import dev.inmo.micro_utils.coroutines.compose.renderComposableAndLinkToContextAndRoot import dev.inmo.postssystem.features.auth.client.ui.* import dev.inmo.micro_utils.coroutines.launchSafelyWithoutExceptions import dev.inmo.micro_utils.coroutines.subscribeSafelyWithoutExceptions import dev.inmo.micro_utils.fsm.common.StatesMachine import dev.inmo.postssystem.features.auth.client.ui.AuthUIError.AuthIncorrect import dev.inmo.postssystem.features.common.common.* import dev.inmo.postssystem.features.common.common.ui.JSView import dev.inmo.postssystem.features.common.common.ui.fsm.* import kotlinx.browser.document import kotlinx.coroutines.* import kotlinx.dom.* import org.jetbrains.compose.web.attributes.InputType import org.jetbrains.compose.web.dom.Text import org.w3c.dom.* @ExperimentalStdlibApi @EagerInitialization val loader = DefaultModuleLoader { factory { AuthView(get(), get(DefaultQualifiers.UIScopeQualifier), getAllDistinct()) } singleWithRandomQualifier { UIFSMHandler.Registrator { strictlyOn(get()) } } } + CommonAuthModuleLoader() class AuthView( private val viewModel: AuthUIViewModel, private val uiScope: CoroutineScope, defaultExceptionsHandlers: Iterable ) : JSView(defaultExceptionsHandlers) { override suspend fun StatesMachine.safeHandleState( htmlElement: HTMLElement, state: AuthUIFSMState ): UIFSMState? { val completion = CompletableDeferred() val usernameState = mutableStateOf("") val passwordState = mutableStateOf("") val disabled = mutableStateOf(true) val errorText = mutableStateOf(null) val root = htmlElement.appendElement("div") {} val composition = renderComposableAndLinkToContextAndRoot(root) { val authBtnDisabled = usernameState.value.isBlank() || passwordState.value.isBlank() Flex( UIKitFlex.Alignment.Horizontal.Center ) { Card( Attrs(UIKitText.Alignment.Horizontal.Center), bodyAttrs = Attrs(UIKitWidth.Fixed.Medium), ) { CardTitle { Text("Log in") } if (errorText.value != null) { CardBadge(Attrs(UIKitLabel.Error)) { Text(errorText.value.toString()) } } StandardInput( InputType.Text, usernameState, disabled, "Username", ) StandardInput( InputType.Password, passwordState, disabled, "Password" ) DefaultButton("Authorise", UIKitButton.Type.Primary, UIKitMargin.Small, disabled = authBtnDisabled) { it.nativeEvent.preventDefault() val serverUrl = document.location ?.run { "$hostname:$port" } if (serverUrl != null) { uiScope.launchSafelyWithoutExceptions { viewModel.initAuth(serverUrl, usernameState.value, passwordState.value) } } } } } } val viewJob = viewModel.currentState.subscribeSafelyWithoutExceptions(uiScope) { when (it) { is AuthUIState.Init -> { disabled.value = false errorText.value = when (it.showError) { AuthUIError.ServerUnavailable -> "Server unavailable" AuthIncorrect -> { passwordState.value = "" "Username or password is incorrect" } null -> null } } AuthUIState.Loading -> { disabled.value = true errorText.value = null } AuthUIState.Authorized -> { completion.complete(state.from) } } } return completion.await().also { composition.dispose() viewJob.cancel() } } }