Skip to content

Commit

Permalink
first application of new generic support in generated code
Browse files Browse the repository at this point in the history
  • Loading branch information
robertfmurdock committed Jul 5, 2023
1 parent 1bfc417 commit 1e25882
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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<D>(
val party: PartyDetails,
val pin: Pin,
val pinList: List<Pin>,
val reload: () -> Unit,
val dispatchFunc: DispatchFunc<out D>,
) : DataPropsBind<PinConfig<D>>(pinConfig.unsafeCast<TMFC>())
where D : SavePinCommand.Dispatcher, D : DeletePinCommand.Dispatcher
external interface PinConfigProps<D> : Props where D : DeletePinCommand.Dispatcher, D : SavePinCommand.Dispatcher {
var party: PartyDetails
var pin: Pin
var pinList: List<Pin>
var reload: () -> Unit
var dispatchFunc: DispatchFunc<out D>
}

private interface DD : SavePinCommand.Dispatcher, DeletePinCommand.Dispatcher
fun <D, C : SuspendAction<D, R>, R> PinConfigProps<D>.dispatch(commandFunc: () -> C, response: (R) -> Unit)
where D : DeletePinCommand.Dispatcher, D : SavePinCommand.Dispatcher = dispatchFunc(commandFunc, response)

private val pinConfig by ntmFC<PinConfig<DD>> { (party, pin, pinList, reload, dispatchFunc) ->
@ReactFunc
val PinConfig by nfc<PinConfigProps<*>> { 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<Json>())

val updatedPin = values.fromJsonDynamic<JsonPinData>().toModel()
val (redirectUrl, setRedirectUrl) = useState<String?>(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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 {
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -298,3 +299,12 @@ fun singleRouteRouter(element: DataProps<*>) = createMemoryRouter(
},
),
)

fun singleRouteRouter(block: ChildrenBuilder.() -> Unit) = createMemoryRouter(
arrayOf(
jso {
path = "*"
this.element = Fragment.create(block)
},
),
)
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<R, P : DataProps<P>>(
data class CouplingQuery<R>(
val query: SuspendAction<CommandDispatcher, R?>,
val toDataprops: (ReloadFunc, DispatchFunc<CommandDispatcher>, R) -> P?,
val toNode: (ReloadFunc, DispatchFunc<CommandDispatcher>, R) -> ReactNode?,
val commander: Commander,
) : DataPropsBind<CouplingQuery<R, P>>(couplingQuery.unsafeCast<TMFC>())
) : DataPropsBind<CouplingQuery<R>>(couplingQuery.unsafeCast<TMFC>())

interface StubDataProps : DataProps<StubDataProps>
fun <P : DataProps<P>, R> CouplingQuery(
query: SuspendAction<CommandDispatcher, R?>,
toDataprops: (ReloadFunc, DispatchFunc<CommandDispatcher>, R) -> P?,
commander: Commander,
) = CouplingQuery(
query = query,
toNode = { r: ReloadFunc, d: DispatchFunc<CommandDispatcher>, result: R ->
toDataprops(r, d, result)?.create()
},
commander = commander,
)

private val couplingQuery by ntmFC { props: CouplingQuery<Any, StubDataProps> ->
fun <R> CouplingQuery(
query: SuspendAction<CommandDispatcher, R?>,
build: ChildrenBuilder.(ReloadFunc, DispatchFunc<CommandDispatcher>, R) -> Unit,
commander: Commander,
) = CouplingQuery<R>(
query = query,
toNode = { r: ReloadFunc, d: DispatchFunc<CommandDispatcher>, result: R ->
react.Fragment.create { build(r, d, result) }
},
commander = commander,
)

private val couplingQuery by ntmFC { props: CouplingQuery<Any> ->
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)
Expand All @@ -38,23 +63,23 @@ private val couplingQuery by ntmFC { props: CouplingQuery<Any, StubDataProps> ->
}
}
add(
DataLoader(getDataAsync, { null }) { state: DataLoadState<StubDataProps?> ->
DataLoader(getDataAsync, { null }) { state: DataLoadState<ReactNode?> ->
animationFrame(state)
},
)
}

private fun <P : DataProps<P>> ChildrenBuilder.animationFrame(state: DataLoadState<P?>) =
private fun <P : DataProps<P>> ChildrenBuilder.animationFrame(state: DataLoadState<ReactNode?>) =
animationFrame {
this.state = state
if (state is ResolvedState) {
resolvedComponent(state)
}
}

private fun <P : DataProps<P>> ChildrenBuilder.resolvedComponent(state: ResolvedState<P?>) {
private fun ChildrenBuilder.resolvedComponent(state: ResolvedState<ReactNode?>) {
when (val result = state.result) {
null -> notFoundContent()
else -> add(result)
else -> +result
}
}
2 changes: 1 addition & 1 deletion coupling-plugins/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit 1e25882

Please sign in to comment.