diff --git a/client/components/src/main/kotlin/com/zegreatrob/coupling/client/components/pin/PinConfig.kt b/client/components/src/main/kotlin/com/zegreatrob/coupling/client/components/pin/PinConfig.kt index ec008ce58d..078e26bf0d 100644 --- a/client/components/src/main/kotlin/com/zegreatrob/coupling/client/components/pin/PinConfig.kt +++ b/client/components/src/main/kotlin/com/zegreatrob/coupling/client/components/pin/PinConfig.kt @@ -13,35 +13,41 @@ import com.zegreatrob.coupling.json.toModel import com.zegreatrob.coupling.json.toSerializable import com.zegreatrob.coupling.model.party.PartyDetails import com.zegreatrob.coupling.model.pin.Pin -import com.zegreatrob.minreact.DataPropsBind -import com.zegreatrob.minreact.TMFC +import com.zegreatrob.minreact.ReactFunc import com.zegreatrob.minreact.add -import com.zegreatrob.minreact.ntmFC +import com.zegreatrob.minreact.nfc +import com.zegreatrob.testmints.action.async.SuspendAction import js.core.jso +import react.Props import react.router.Navigate import react.router.dom.usePrompt import react.useState import kotlin.js.Json -data class PinConfig( - val party: PartyDetails, - val pin: Pin, - val pinList: List, - val reload: () -> Unit, - val dispatchFunc: DispatchFunc, -) : DataPropsBind>(pinConfig.unsafeCast()) - where D : SavePinCommand.Dispatcher, D : DeletePinCommand.Dispatcher +external interface PinConfigProps : Props where D : DeletePinCommand.Dispatcher, D : SavePinCommand.Dispatcher { + var party: PartyDetails + var pin: Pin + var pinList: List + var reload: () -> Unit + var dispatchFunc: DispatchFunc +} -private interface DD : SavePinCommand.Dispatcher, DeletePinCommand.Dispatcher +fun , R> PinConfigProps.dispatch(commandFunc: () -> C, response: (R) -> Unit) + where D : DeletePinCommand.Dispatcher, D : SavePinCommand.Dispatcher = dispatchFunc(commandFunc, response) -private val pinConfig by ntmFC> { (party, pin, pinList, reload, dispatchFunc) -> +@ReactFunc +val PinConfig by nfc> { props -> + val party = props.party + val pin = props.pin + val pinList = props.pinList + val reload = props.reload val (values, onChange) = useForm(pin.toSerializable().toJsonDynamic().unsafeCast()) val updatedPin = values.fromJsonDynamic().toModel() val (redirectUrl, setRedirectUrl) = useState(null) - val onSubmit = dispatchFunc({ SavePinCommand(party.id, updatedPin) }) { reload() } + val onSubmit = props.dispatch({ SavePinCommand(party.id, updatedPin) }) { reload() } val onRemove = pin.id?.let { pinId -> - dispatchFunc({ DeletePinCommand(party.id, pinId) }) { setRedirectUrl(party.id.pinListPath()) } + props.dispatch({ DeletePinCommand(party.id, pinId) }) { setRedirectUrl(party.id.pinListPath()) } .requireConfirmation("Are you sure you want to delete this pin?") } usePrompt( diff --git a/client/components/src/test/kotlin/com/zegreatrob/coupling/client/components/pin/PinConfigEditorTest.kt b/client/components/src/test/kotlin/com/zegreatrob/coupling/client/components/pin/PinConfigEditorTest.kt index 27244478d2..1f19daae4c 100644 --- a/client/components/src/test/kotlin/com/zegreatrob/coupling/client/components/pin/PinConfigEditorTest.kt +++ b/client/components/src/test/kotlin/com/zegreatrob/coupling/client/components/pin/PinConfigEditorTest.kt @@ -28,9 +28,15 @@ class PinConfigEditorTest { }) exercise { render( RouterProvider.create { - router = singleRouteRouter( - PinConfig(party, pin, emptyList(), {}, StubDispatchFunc()), - ) + router = singleRouteRouter { + PinConfig( + party = party, + pin = pin, + pinList = emptyList(), + reload = {}, + dispatchFunc = StubDispatchFunc(), + ) + } }, ) } verify { @@ -45,9 +51,7 @@ class PinConfigEditorTest { }) exercise { render( RouterProvider.create { - router = singleRouteRouter( - PinConfig(party, pin, emptyList(), {}, StubDispatchFunc()), - ) + router = singleRouteRouter { PinConfig(party, pin, emptyList(), {}, StubDispatchFunc()) } }, ) } verify { @@ -67,9 +71,7 @@ class PinConfigEditorTest { }) { render( RouterProvider.create { - router = singleRouteRouter( - PinConfig(party, pin, emptyList(), {}, stubDispatcher.func()), - ) + router = singleRouteRouter { PinConfig(party, pin, emptyList(), {}, stubDispatcher.func()) } }, ) actor.type(screen.getByLabelText("Name"), newName) diff --git a/client/components/src/test/kotlin/com/zegreatrob/coupling/client/components/player/PlayerConfigTest.kt b/client/components/src/test/kotlin/com/zegreatrob/coupling/client/components/player/PlayerConfigTest.kt index 22fed0bb79..349415288d 100644 --- a/client/components/src/test/kotlin/com/zegreatrob/coupling/client/components/player/PlayerConfigTest.kt +++ b/client/components/src/test/kotlin/com/zegreatrob/coupling/client/components/player/PlayerConfigTest.kt @@ -29,6 +29,7 @@ import com.zegreatrob.wrapper.testinglibrary.userevent.UserEvent import js.core.jso import kotlinx.browser.window import org.w3c.dom.Window +import react.ChildrenBuilder import react.Fragment import react.ReactNode import react.create @@ -298,3 +299,12 @@ fun singleRouteRouter(element: DataProps<*>) = createMemoryRouter( }, ), ) + +fun singleRouteRouter(block: ChildrenBuilder.() -> Unit) = createMemoryRouter( + arrayOf( + jso { + path = "*" + this.element = Fragment.create(block) + }, + ), +) diff --git a/client/src/main/kotlin/com/zegreatrob/coupling/client/pin/PinPage.kt b/client/src/main/kotlin/com/zegreatrob/coupling/client/pin/PinPage.kt index e9cafba526..857d21fea1 100644 --- a/client/src/main/kotlin/com/zegreatrob/coupling/client/pin/PinPage.kt +++ b/client/src/main/kotlin/com/zegreatrob/coupling/client/pin/PinPage.kt @@ -19,10 +19,10 @@ val PinPage = partyPageFunction { props, partyId -> pinList() } }, - toDataprops = { reload, commandFunc, result -> - val pinList = result.party?.pinList?.elements ?: return@CouplingQuery null + build = { reload, commandFunc, result -> + val pinList = result.party?.pinList?.elements ?: return@CouplingQuery PinConfig( - party = result.party?.details?.data ?: return@CouplingQuery null, + party = result.party?.details?.data ?: return@CouplingQuery, pinList = pinList, pin = pinList.firstOrNull { it.id == pinId } ?: Pin(), reload = reload, diff --git a/client/src/main/kotlin/com/zegreatrob/coupling/client/routing/CouplingDataLoadWrapper.kt b/client/src/main/kotlin/com/zegreatrob/coupling/client/routing/CouplingDataLoadWrapper.kt index 304082bc02..7e43b14fb9 100644 --- a/client/src/main/kotlin/com/zegreatrob/coupling/client/routing/CouplingDataLoadWrapper.kt +++ b/client/src/main/kotlin/com/zegreatrob/coupling/client/routing/CouplingDataLoadWrapper.kt @@ -3,6 +3,7 @@ package com.zegreatrob.coupling.client.routing import com.zegreatrob.coupling.client.CommandDispatcher import com.zegreatrob.coupling.client.DecoratedDispatchFunc import com.zegreatrob.coupling.client.components.DispatchFunc +import com.zegreatrob.coupling.client.create import com.zegreatrob.minreact.DataProps import com.zegreatrob.minreact.DataPropsBind import com.zegreatrob.minreact.TMFC @@ -16,20 +17,44 @@ 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.ReactNode +import react.create import react.useCallback -data class CouplingQuery>( +data class CouplingQuery( val query: SuspendAction, - val toDataprops: (ReloadFunc, DispatchFunc, R) -> P?, + val toNode: (ReloadFunc, DispatchFunc, R) -> ReactNode?, val commander: Commander, -) : DataPropsBind>(couplingQuery.unsafeCast()) +) : DataPropsBind>(couplingQuery.unsafeCast()) -interface StubDataProps : DataProps +fun

, R> CouplingQuery( + query: SuspendAction, + toDataprops: (ReloadFunc, DispatchFunc, R) -> P?, + commander: Commander, +) = CouplingQuery( + query = query, + toNode = { r: ReloadFunc, d: DispatchFunc, result: R -> + toDataprops(r, d, result)?.create() + }, + commander = commander, +) -private val couplingQuery by ntmFC { props: CouplingQuery -> +fun CouplingQuery( + query: SuspendAction, + build: ChildrenBuilder.(ReloadFunc, DispatchFunc, R) -> Unit, + commander: Commander, +) = CouplingQuery( + query = query, + toNode = { r: ReloadFunc, d: DispatchFunc, result: R -> + react.Fragment.create { build(r, d, result) } + }, + commander = commander, +) + +private val couplingQuery by ntmFC { props: CouplingQuery -> val (query, toDataprops, commander) = props - val getDataAsync: suspend (DataLoaderTools) -> StubDataProps? = useCallback { tools -> + val getDataAsync: suspend (DataLoaderTools) -> ReactNode? = useCallback { tools -> val dispatchFunc = DecoratedDispatchFunc(commander::tracingDispatcher, tools) commander.tracingDispatcher() .execute(query) @@ -38,13 +63,13 @@ private val couplingQuery by ntmFC { props: CouplingQuery -> } } add( - DataLoader(getDataAsync, { null }) { state: DataLoadState -> + DataLoader(getDataAsync, { null }) { state: DataLoadState -> animationFrame(state) }, ) } -private fun

> ChildrenBuilder.animationFrame(state: DataLoadState) = +private fun

> ChildrenBuilder.animationFrame(state: DataLoadState) = animationFrame { this.state = state if (state is ResolvedState) { @@ -52,9 +77,9 @@ private fun

> ChildrenBuilder.animationFrame(state: DataLoadSta } } -private fun

> ChildrenBuilder.resolvedComponent(state: ResolvedState) { +private fun ChildrenBuilder.resolvedComponent(state: ResolvedState) { when (val result = state.result) { null -> notFoundContent() - else -> add(result) + else -> +result } } diff --git a/coupling-plugins/gradle/libs.versions.toml b/coupling-plugins/gradle/libs.versions.toml index fd89631eb3..c2e038dedc 100644 --- a/coupling-plugins/gradle/libs.versions.toml +++ b/coupling-plugins/gradle/libs.versions.toml @@ -8,7 +8,7 @@ org-jlleitschuh-gradle-ktlint = "11.0.0" com-fasterxml-jackson-core-jackson-databind = "com.fasterxml.jackson.core:jackson-databind:2.15.2" com-github-ben-manes-gradle-versions-plugin = "com.github.ben-manes:gradle-versions-plugin:0.47.0" com-soywiz-korlibs-klock = "com.soywiz.korlibs.klock:klock:4.0.7" -com-zegreatrob-jsmints-jsmints-bom = "com.zegreatrob.jsmints:jsmints-bom:3.5.75" +com-zegreatrob-jsmints-jsmints-bom = "com.zegreatrob.jsmints:jsmints-bom:3.5.76" com-zegreatrob-testmints-testmints-bom = "com.zegreatrob.testmints:testmints-bom:9.3.41" com-zegreatrob-tools-tools-bom = "com.zegreatrob.tools:tools-bom:0.2.41" org-ajoberstar-grgit-gradle-plugin = "org.ajoberstar.grgit:org.ajoberstar.grgit.gradle.plugin:5.0.0" diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 506ff16cd0..6e1ef1559f 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:3.5.4" com-soywiz-korlibs-klock = "com.soywiz.korlibs.klock:klock:4.0.7" -com-zegreatrob-jsmints-jsmints-bom = "com.zegreatrob.jsmints:jsmints-bom:3.5.75" +com-zegreatrob-jsmints-jsmints-bom = "com.zegreatrob.jsmints:jsmints-bom:3.5.76" com-zegreatrob-testmints-testmints-bom = "com.zegreatrob.testmints:testmints-bom:9.3.41" io-github-microutils-kotlin-logging = "io.github.microutils:kotlin-logging:3.0.5" io-ktor-ktor-bom = "io.ktor:ktor-bom:2.3.2"