include compose

This commit is contained in:
2022-01-22 20:19:50 +06:00
parent 4f96d2ce76
commit 0716035f0b
9 changed files with 119 additions and 106 deletions

View File

@@ -1,34 +1,28 @@
package dev.inmo.postssystem.client.fsm.ui
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import dev.inmo.jsuikit.elements.*
import dev.inmo.jsuikit.modifiers.UIKitButton
import dev.inmo.jsuikit.modifiers.UIKitMargin
import dev.inmo.postssystem.client.ui.fsm.*
import dev.inmo.postssystem.client.utils.HTMLViewContainer
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.client.utils.renderComposableAndLinkToContext
import kotlinx.browser.document
import kotlinx.coroutines.*
import kotlinx.dom.clear
import kotlinx.html.*
import kotlinx.html.dom.append
import kotlinx.html.js.form
import kotlinx.html.js.onClickFunction
import org.jetbrains.compose.web.attributes.InputType
import org.jetbrains.compose.web.dom.Form
import org.w3c.dom.*
class AuthView(
private val viewModel: AuthUIViewModel,
private val uiScope: CoroutineScope
) : JSView<AuthUIFSMState>() {
private val usernameInput
get() = document.getElementById("authUsername") as? HTMLInputElement
private val passwordInput
get() = document.getElementById("authPassword") as? HTMLInputElement
private val authButton
get() = document.getElementById("authButton")
private val errorBadge
get() = document.getElementById("errorBadge") as? HTMLElement
private val progressBarDiv
get() = document.getElementById("progressBar") as? HTMLDivElement
override suspend fun StatesMachine<in UIFSMState>.safeHandleState(
htmlElement: HTMLElement,
@@ -38,42 +32,36 @@ class AuthView(
val completion = CompletableDeferred<UIFSMState?>()
htmlElement.clear()
htmlElement.append {
form(classes = "vertical_container") {
div(classes = "mdl-textfield mdl-js-textfield mdl-textfield--floating-label") {
input(type = InputType.text, classes = "mdl-textfield__input") {
id = "authUsername"
}
label(classes = "mdl-textfield__label") {
+"Имя пользователя"
}
val usernameState = mutableStateOf("")
val passwordState = mutableStateOf("")
val usernameDisabled = mutableStateOf(true)
val passwordDisabled = mutableStateOf(true)
val authBtnDisabled = remember {
usernameState.value.isNotBlank() && passwordState.value.isNotBlank()
}
val errorText = mutableStateOf<String?>(null)
val composition = renderComposableAndLinkToContext(htmlElement) {
Form {
TextField(
InputType.Text,
usernameState,
disabledState = usernameDisabled
)
TextField(
InputType.Password,
passwordState,
disabledState = passwordDisabled
)
if (errorText.value != null) {
Label.Error.draw(errorText.value.toString(), UIKitMargin.Small.Bottom)
}
div(classes = "mdl-textfield mdl-js-textfield mdl-textfield--floating-label") {
input(type = InputType.password, classes = "mdl-textfield__input") {
id = "authPassword"
}
label(classes = "mdl-textfield__label") {
+"Пароль"
}
}
div(classes = "mdl-progress mdl-js-progress") {
id = "progressBar"
}
span(classes = "material-icons mdl-badge mdl-badge--overlap gone") {
id = "errorBadge"
attributes["data-badge"] = "!"
}
button(classes = "mdl-button mdl-js-button mdl-button--raised") {
+"Авторизоваться"
id = "authButton"
onClickFunction = {
it.preventDefault()
val serverUrl = document.location ?.run { "$hostname:$port" }
val username = usernameInput ?.value
val password = passwordInput ?.value
if (serverUrl != null && username != null && password != null) {
uiScope.launchSafelyWithoutExceptions { viewModel.initAuth(serverUrl, username, password) }
}
DefaultButton("Authorise", UIKitButton.Type.Primary, disabled = authBtnDisabled) {
val serverUrl = document.location ?.run { "$hostname:$port" }
if (serverUrl != null) {
uiScope.launchSafelyWithoutExceptions { viewModel.initAuth(serverUrl, usernameState.value, passwordState.value) }
}
}
}
@@ -82,38 +70,27 @@ class AuthView(
val viewJob = viewModel.currentState.subscribeSafelyWithoutExceptions(uiScope) {
when (it) {
is InitAuthUIState -> {
usernameInput ?.removeAttribute("disabled")
passwordInput ?.removeAttribute("disabled")
authButton ?.removeAttribute("disabled")
errorBadge ?.apply {
when (it.showError) {
ServerUnavailableAuthUIError -> {
classList.remove("gone")
innerText = "Сервер недоступен"
}
AuthIncorrectAuthUIError -> {
classList.remove("gone")
innerText = "Данные некорректны"
}
null -> classList.add("gone")
}
usernameDisabled.value = false
passwordDisabled.value = false
errorText.value = when (it.showError) {
ServerUnavailableAuthUIError -> "Сервер недоступен"
AuthIncorrectAuthUIError -> "Данные некорректны"
null -> null
}
progressBarDiv ?.classList ?.add("gone")
}
LoadingAuthUIState -> {
usernameInput ?.setAttribute("disabled", "")
passwordInput ?.setAttribute("disabled", "")
authButton ?.setAttribute("disabled", "")
errorBadge ?.classList ?.add("gone")
progressBarDiv ?.classList ?.remove("gone")
usernameDisabled.value = true
passwordDisabled.value = true
errorText.value = null
}
AuthorizedAuthUIState -> {
htmlElement.clear()
completion.complete(state.from)
}
}
}
return completion.await().also {
composition.dispose()
viewJob.cancel()
}
}

View File

@@ -0,0 +1,25 @@
package dev.inmo.postssystem.client.utils
import androidx.compose.runtime.*
import kotlinx.coroutines.*
import org.jetbrains.compose.web.dom.DOMScope
import org.w3c.dom.Element
import kotlin.coroutines.CoroutineContext
fun Composition.linkWithJob(job: Job) {
job.invokeOnCompletion {
this@linkWithJob.dispose()
}
}
fun Composition.linkWithContext(coroutineContext: CoroutineContext) = linkWithJob(coroutineContext.job)
suspend fun <TElement : Element> renderComposableAndLinkToContext(
root: TElement,
monotonicFrameClock: MonotonicFrameClock = DefaultMonotonicFrameClock,
content: @Composable DOMScope<TElement>.() -> Unit
): Composition = org.jetbrains.compose.web.renderComposable(root, monotonicFrameClock, content).apply {
linkWithContext(
currentCoroutineContext()
)
}