From 69293a58206141c9a4e6ff94bb0a4011be7cbf8c Mon Sep 17 00:00:00 2001 From: Rob Murdock Date: Mon, 10 Jul 2023 09:34:26 -0400 Subject: [PATCH] using the new data loader from minreact --- .../components/WaitForAsyncReactComponent.kt | 41 ++++++++++++----- .../components/external/reactdnd/Dsl.kt | 3 +- .../external/reactmarkdown/External.kt | 3 +- .../pairassignments/PairAssignments.kt | 39 ++++++++++------ .../coupling/client/graphql/GraphIqlPage.kt | 29 +++++++----- .../coupling/client/graphql/GraphiQL.kt | 3 +- .../client/routing/CouplingDataLoadWrapper.kt | 24 ++++------ .../client/slack/SlackCallbackPage.kt | 44 ++++++++++--------- gradle/libs.versions.toml | 2 +- 9 files changed, 111 insertions(+), 77 deletions(-) diff --git a/client/components/src/jsMain/kotlin/com/zegreatrob/coupling/client/components/WaitForAsyncReactComponent.kt b/client/components/src/jsMain/kotlin/com/zegreatrob/coupling/client/components/WaitForAsyncReactComponent.kt index 1446cda566..0f4362c006 100644 --- a/client/components/src/jsMain/kotlin/com/zegreatrob/coupling/client/components/WaitForAsyncReactComponent.kt +++ b/client/components/src/jsMain/kotlin/com/zegreatrob/coupling/client/components/WaitForAsyncReactComponent.kt @@ -1,6 +1,8 @@ package com.zegreatrob.coupling.client.components -import com.zegreatrob.minreact.create +import com.zegreatrob.minreact.ReactFunc +import com.zegreatrob.minreact.nfc +import com.zegreatrob.react.dataloader.DataLoadState import com.zegreatrob.react.dataloader.DataLoader import com.zegreatrob.react.dataloader.EmptyState import com.zegreatrob.react.dataloader.PendingState @@ -9,23 +11,40 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.withTimeout import react.ChildrenBuilder import react.ElementType +import react.Fragment import react.Props +import react.ReactNode +import react.create import react.dom.html.ReactHTML.div fun ChildrenBuilder.waitForAsyncReactComponent( getComponent: () -> ElementType?, - useComponent: ChildrenBuilder.(ElementType) -> Unit, + useComponent: (ElementType) -> ReactNode, ) { - +DataLoader({ waitForComponent(getComponent) }, { null }) { state -> - when (state) { - is EmptyState -> div { +"Preparing component" } - is PendingState -> div { +"Pending component" } - is ResolvedState -> - state.result - ?.let { useComponent(it) } - ?: div { +"Error finding component." } + DataLoader({ waitForComponent(getComponent) }, { null }, child = { state -> + Fragment.create { + AsyncReactComponent(state, useComponent) } - }.create() + }) +} + +external interface AsyncReactComponentProps : Props { + var state: DataLoadState?> + var useComponent: (ElementType) -> ReactNode +} + +@ReactFunc +val AsyncReactComponent by nfc> { props -> + val state = props.state + val useComponent = props.useComponent + when (state) { + is EmptyState -> div { +"Preparing component" } + is PendingState -> div { +"Pending component" } + is ResolvedState -> + state.result + ?.let { Fragment { child(useComponent(it)) } } + ?: div { +"Error finding component." } + } } private suspend fun waitForComponent(getComponent: () -> ElementType?): ElementType? = diff --git a/client/components/src/jsMain/kotlin/com/zegreatrob/coupling/client/components/external/reactdnd/Dsl.kt b/client/components/src/jsMain/kotlin/com/zegreatrob/coupling/client/components/external/reactdnd/Dsl.kt index 51eb90a387..6667bfb083 100644 --- a/client/components/src/jsMain/kotlin/com/zegreatrob/coupling/client/components/external/reactdnd/Dsl.kt +++ b/client/components/src/jsMain/kotlin/com/zegreatrob/coupling/client/components/external/reactdnd/Dsl.kt @@ -7,6 +7,7 @@ import kotlinx.coroutines.async import kotlinx.coroutines.await import react.ElementType import react.FC +import react.create import kotlin.js.Json import kotlin.js.Promise import kotlin.js.json @@ -22,7 +23,7 @@ val DndProvider: ElementType = FC { props -> +props.children } else { waitForAsyncReactComponent({ runCatching { reactDnd.getCompleted().dndProvider }.getOrNull() }) { component -> - component { +props } + component.create { +props } } } } diff --git a/client/components/src/jsMain/kotlin/com/zegreatrob/coupling/client/components/external/reactmarkdown/External.kt b/client/components/src/jsMain/kotlin/com/zegreatrob/coupling/client/components/external/reactmarkdown/External.kt index c72ffe5e6a..739025355f 100644 --- a/client/components/src/jsMain/kotlin/com/zegreatrob/coupling/client/components/external/reactmarkdown/External.kt +++ b/client/components/src/jsMain/kotlin/com/zegreatrob/coupling/client/components/external/reactmarkdown/External.kt @@ -6,9 +6,10 @@ import org.w3c.dom.get import react.ElementType import react.FC import react.Props +import react.create val Markdown: ElementType = FC { props -> waitForAsyncReactComponent({ window["ReactMarkdown"].unsafeCast?>() }) { component -> - component { +props } + component.create { +props } } } diff --git a/client/components/src/jsMain/kotlin/com/zegreatrob/coupling/client/components/pairassignments/PairAssignments.kt b/client/components/src/jsMain/kotlin/com/zegreatrob/coupling/client/components/pairassignments/PairAssignments.kt index cf33528c2a..29ee351f2d 100644 --- a/client/components/src/jsMain/kotlin/com/zegreatrob/coupling/client/components/pairassignments/PairAssignments.kt +++ b/client/components/src/jsMain/kotlin/com/zegreatrob/coupling/client/components/pairassignments/PairAssignments.kt @@ -4,6 +4,7 @@ import com.zegreatrob.coupling.action.pairassignmentdocument.DeletePairAssignmen import com.zegreatrob.coupling.client.components.Controls import com.zegreatrob.coupling.client.components.ServerMessage import com.zegreatrob.coupling.client.components.external.reactdnd.DndProvider +import com.zegreatrob.coupling.client.components.external.reactdndhtml5backend.ReactDndHtml5BackendModule import com.zegreatrob.coupling.client.components.external.reactdndhtml5backend.html5BackendDeferred import com.zegreatrob.coupling.client.components.party.PartyBrowser import com.zegreatrob.coupling.client.components.player.PlayerRoster @@ -14,8 +15,8 @@ import com.zegreatrob.coupling.model.pairassignmentdocument.players import com.zegreatrob.coupling.model.party.PartyDetails import com.zegreatrob.coupling.model.player.Player import com.zegreatrob.minreact.ReactFunc -import com.zegreatrob.minreact.add import com.zegreatrob.minreact.nfc +import com.zegreatrob.react.dataloader.DataLoadState import com.zegreatrob.react.dataloader.DataLoader import com.zegreatrob.react.dataloader.EmptyState import com.zegreatrob.react.dataloader.PendingState @@ -24,6 +25,7 @@ import csstype.PropertiesBuilder import emotion.css.ClassName import react.Props import react.PropsWithChildren +import react.PropsWithValue import react.dom.html.ReactHTML.div import web.cssom.Color import web.cssom.Display @@ -66,22 +68,31 @@ val PairAssignments by nfc { props -> } val Html5DndProvider by nfc { props -> - add( - DataLoader({ html5BackendDeferred.await() }, { null }) { state -> - when (state) { - is EmptyState -> div { +"Preparing component" } - is PendingState -> div { +"Pending component" } - is ResolvedState -> state.result?.let { - DndProvider { - backend = it.html5Backend - +props.children - } - } - } - }, + DataLoader( + getDataAsync = { html5BackendDeferred.await() }, + errorData = { null }, + child = { state -> DndProviderLoading.create(value = state, children = { +props.children }) }, ) } +external interface DndProviderLoadingProps : + PropsWithValue>, + PropsWithChildren + +@ReactFunc +val DndProviderLoading by nfc { props -> + when (val state = props.value) { + is EmptyState -> div { +"Preparing component" } + is PendingState -> div { +"Pending component" } + is ResolvedState -> state.result?.let { + DndProvider { + backend = it.html5Backend + +props.children + } + } + } +} + private fun PropertiesBuilder.pairAssignmentStyles() { display = Display.inlineBlock minHeight = 100.vh diff --git a/client/src/jsMain/kotlin/com/zegreatrob/coupling/client/graphql/GraphIqlPage.kt b/client/src/jsMain/kotlin/com/zegreatrob/coupling/client/graphql/GraphIqlPage.kt index f0d84bfede..d19fc87441 100644 --- a/client/src/jsMain/kotlin/com/zegreatrob/coupling/client/graphql/GraphIqlPage.kt +++ b/client/src/jsMain/kotlin/com/zegreatrob/coupling/client/graphql/GraphIqlPage.kt @@ -3,7 +3,6 @@ package com.zegreatrob.coupling.client.graphql import com.zegreatrob.coupling.client.components.external.auth0.react.useAuth0Data import com.zegreatrob.coupling.client.components.external.w3c.WindowFunctions import com.zegreatrob.coupling.client.routing.PageProps -import com.zegreatrob.minreact.add import com.zegreatrob.minreact.nfc import com.zegreatrob.react.dataloader.DataLoadState import com.zegreatrob.react.dataloader.DataLoader @@ -13,6 +12,8 @@ import com.zegreatrob.react.dataloader.ResolvedState import emotion.react.css import kotlinx.browser.window import org.w3c.dom.get +import react.PropsWithValue +import react.create import react.dom.html.ReactHTML.div import web.cssom.TextAlign import web.cssom.vh @@ -26,17 +27,21 @@ val GraphIQLPage by nfc { textAlign = TextAlign.left height = 100.vh } - add( - DataLoader({ auth0Data.getAccessTokenSilently() }, { "" }) { state: DataLoadState -> - when (state) { - is EmptyState -> div() - is PendingState -> +"Loading authorization..." - is ResolvedState -> GraphiQL { - this.editorTheme = "dracula" - this.fetcher = createGraphiQLFetcher(graphQlUrl, state.result) - } - } - }, + DataLoader( + getDataAsync = { auth0Data.getAccessTokenSilently() }, + errorData = { "" }, + child = GraphIQLPageLoader::create, ) } } + +val GraphIQLPageLoader by nfc>> { props -> + when (val state = props.value) { + is EmptyState -> div() + is PendingState -> +"Loading authorization..." + is ResolvedState -> GraphiQL { + this.editorTheme = "dracula" + this.fetcher = createGraphiQLFetcher(graphQlUrl, state.result) + } + } +} diff --git a/client/src/jsMain/kotlin/com/zegreatrob/coupling/client/graphql/GraphiQL.kt b/client/src/jsMain/kotlin/com/zegreatrob/coupling/client/graphql/GraphiQL.kt index 40226f9365..622c334298 100644 --- a/client/src/jsMain/kotlin/com/zegreatrob/coupling/client/graphql/GraphiQL.kt +++ b/client/src/jsMain/kotlin/com/zegreatrob/coupling/client/graphql/GraphiQL.kt @@ -6,12 +6,13 @@ import org.w3c.dom.get import react.ElementType import react.FC import react.Props +import react.create import kotlin.js.Json import kotlin.js.Promise val GraphiQL: ElementType = FC { props -> waitForAsyncReactComponent({ window["GraphiQL"].unsafeCast?>() }) { component -> - component { +props } + component.create { +props } } } diff --git a/client/src/jsMain/kotlin/com/zegreatrob/coupling/client/routing/CouplingDataLoadWrapper.kt b/client/src/jsMain/kotlin/com/zegreatrob/coupling/client/routing/CouplingDataLoadWrapper.kt index 6e1aa77659..36059ceba9 100644 --- a/client/src/jsMain/kotlin/com/zegreatrob/coupling/client/routing/CouplingDataLoadWrapper.kt +++ b/client/src/jsMain/kotlin/com/zegreatrob/coupling/client/routing/CouplingDataLoadWrapper.kt @@ -4,7 +4,6 @@ import com.zegreatrob.coupling.client.CommandDispatcher import com.zegreatrob.coupling.client.DecoratedDispatchFunc import com.zegreatrob.coupling.client.components.DispatchFunc import com.zegreatrob.minreact.ReactFunc -import com.zegreatrob.minreact.add import com.zegreatrob.minreact.nfc import com.zegreatrob.react.dataloader.DataLoadState import com.zegreatrob.react.dataloader.DataLoader @@ -13,9 +12,10 @@ import com.zegreatrob.react.dataloader.ReloadFunc import com.zegreatrob.react.dataloader.ResolvedState import com.zegreatrob.testmints.action.async.SuspendAction import com.zegreatrob.testmints.action.async.execute -import react.ChildrenBuilder import react.Props +import react.PropsWithValue import react.ReactNode +import react.create import react.useCallback external interface CouplingQueryProps : Props { @@ -36,24 +36,18 @@ val CouplingQuery by nfc> { props -> toNode(tools.reloadData, dispatchFunc, value) } } - add( - DataLoader(getDataAsync, { null }) { state: DataLoadState -> - animationFrame(state) - }, - ) + DataLoader(getDataAsync, { null }, child = CouplingQueryLoadState::create) } -private fun ChildrenBuilder.animationFrame(state: DataLoadState) = +val CouplingQueryLoadState by nfc>> { props -> + val state = props.value animationFrame { this.state = state if (state is ResolvedState) { - resolvedComponent(state) + when (val result = state.result) { + null -> notFoundContent() + else -> +result + } } } - -private fun ChildrenBuilder.resolvedComponent(state: ResolvedState) { - when (val result = state.result) { - null -> notFoundContent() - else -> +result - } } diff --git a/client/src/jsMain/kotlin/com/zegreatrob/coupling/client/slack/SlackCallbackPage.kt b/client/src/jsMain/kotlin/com/zegreatrob/coupling/client/slack/SlackCallbackPage.kt index 81186bf11c..fcfd99cc1d 100644 --- a/client/src/jsMain/kotlin/com/zegreatrob/coupling/client/slack/SlackCallbackPage.kt +++ b/client/src/jsMain/kotlin/com/zegreatrob/coupling/client/slack/SlackCallbackPage.kt @@ -7,13 +7,15 @@ import com.zegreatrob.coupling.client.components.external.reactmarkdown.Markdown import com.zegreatrob.coupling.client.components.loadMarkdownString import com.zegreatrob.coupling.client.components.slack.ReturnToCouplingButton import com.zegreatrob.coupling.client.routing.PageProps -import com.zegreatrob.minreact.add import com.zegreatrob.minreact.nfc +import com.zegreatrob.react.dataloader.DataLoadState import com.zegreatrob.react.dataloader.DataLoader import com.zegreatrob.react.dataloader.EmptyState import com.zegreatrob.react.dataloader.PendingState import com.zegreatrob.react.dataloader.ResolvedState import react.Props +import react.PropsWithValue +import react.create import react.router.dom.useSearchParams val SlackCallbackPage by nfc { props -> @@ -24,31 +26,31 @@ val SlackCallbackPage by nfc { props -> if (code == null || state == null) { +"code and state missing" } else { - add( - DataLoader( - getDataAsync = { - props.commander.tracingDispatcher().sdk.perform( - GrantSlackAccessCommand(code, state), - ) - }, - errorData = { VoidResult.Rejected }, - children = { - when (it) { - is EmptyState -> +"Empty" - is PendingState -> +"Pending" - is ResolvedState -> when (it.result) { - VoidResult.Accepted -> SlackInstallSuccess {} - VoidResult.Rejected -> +"Rejected" - CommandResult.Unauthorized -> +"Unauthorized" - } - } - }, - ), + DataLoader( + getDataAsync = { + props.commander.tracingDispatcher().sdk.perform( + GrantSlackAccessCommand(code, state), + ) + }, + errorData = { VoidResult.Rejected }, + child = SlackCallbackLoadContent::create, ) } } } +val SlackCallbackLoadContent by nfc>> { props -> + when (val data = props.value) { + is EmptyState -> +"Empty" + is PendingState -> +"Pending" + is ResolvedState -> when (data.result) { + VoidResult.Accepted -> SlackInstallSuccess {} + VoidResult.Rejected -> +"Rejected" + CommandResult.Unauthorized -> +"Unauthorized" + } + } +} + val SlackInstallSuccess by nfc { Markdown { +loadMarkdownString("InstallSuccess") } ReturnToCouplingButton { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ee2450a56a..82a48232d1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ com-benasher44-uuid = "com.benasher44:uuid:0.7.1" com-fasterxml-jackson-core-jackson-databind = "com.fasterxml.jackson.core:jackson-databind:2.15.2" com-github-ajalt-clikt-clikt = "com.github.ajalt.clikt:clikt:4.0.0" com-soywiz-korlibs-klock = "com.soywiz.korlibs.klock:klock:4.0.8" -com-zegreatrob-jsmints-jsmints-bom = "com.zegreatrob.jsmints:jsmints-bom:4.2.1" +com-zegreatrob-jsmints-jsmints-bom = "com.zegreatrob.jsmints:jsmints-bom:4.3.0" com-zegreatrob-testmints-testmints-bom = "com.zegreatrob.testmints:testmints-bom:10.0.1" io-github-microutils-kotlin-logging = "io.github.microutils:kotlin-logging:3.0.5" io-ktor-ktor-bom = "io.ktor:ktor-bom:2.3.2"