Skip to content

Commit

Permalink
feat: initial javascript support
Browse files Browse the repository at this point in the history
  • Loading branch information
programadorthi committed Dec 22, 2023
1 parent 080dc9c commit 98f9ddf
Show file tree
Hide file tree
Showing 11 changed files with 160 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import dev.programadorthi.routing.core.application.application
import dev.programadorthi.routing.core.application.call
import dev.programadorthi.routing.core.previousCall
import dev.programadorthi.routing.core.route
import dev.programadorthi.routing.core.toNeglect
import io.ktor.util.KtorDsl
import io.ktor.util.pipeline.PipelineContext
import io.ktor.util.pipeline.execute
Expand Down Expand Up @@ -37,10 +38,23 @@ public fun Route.composable(
if (call.routeMethod != StackRouteMethod.Pop) {
call.content = { body(this) }
} else {
// Checking for previous ApplicationCall and redirecting to it
val previous = previousCall()
if (previous != null) {
application.execute(previous)
// Checking for previous ApplicationCall to recompose it
val popDestination = previousCall()
if (popDestination != null) {
// We need do some things here:
// 1. Use previous call name, uri and route method
// 2. Put pop call attributes and parameters to previous call consume
// 3. Neglect the call to avoid put again on the stack
application.execute(
ApplicationCall(
application = application,
name = popDestination.name,
uri = popDestination.uri,
routeMethod = popDestination.routeMethod,
attributes = call.attributes,
parameters = call.parameters,
).toNeglect(neglect = true)
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ internal class StackManager(

fun update(call: ApplicationCall) {
// Check if route should be out of the stack
if (call.stackNeglect || call.routeMethod == StackRouteMethod.Pop) {
if (call.neglect || call.routeMethod == StackRouteMethod.Pop) {
return
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import io.ktor.util.AttributeKey

internal val StackNeglectAttributeKey: AttributeKey<Unit> = AttributeKey("StackNeglectAttributeKey")

public var ApplicationCall.stackNeglect: Boolean
public var ApplicationCall.neglect: Boolean
get() = attributes.contains(StackNeglectAttributeKey)
internal set(value) = if (value) {
attributes.put(StackNeglectAttributeKey, Unit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public fun Routing.push(
uri = path,
parameters = parameters,
routeMethod = StackRouteMethod.Push,
).tryNeglect(neglect)
).toNeglect(neglect)
)
}

Expand All @@ -44,7 +44,7 @@ public fun Routing.pushNamed(
name = name,
parameters = parameters,
routeMethod = StackRouteMethod.Push,
).tryNeglect(neglect)
).toNeglect(neglect)
)
}

Expand All @@ -60,7 +60,7 @@ public fun Routing.replace(
uri = path,
parameters = parameters,
routeMethod = StackRouteMethod.Replace,
).tryNeglect(neglect)
).toNeglect(neglect)
)
}

Expand All @@ -76,7 +76,7 @@ public fun Routing.replaceAll(
uri = path,
parameters = parameters,
routeMethod = StackRouteMethod.ReplaceAll,
).tryNeglect(neglect)
).toNeglect(neglect)
)
}

Expand All @@ -92,7 +92,7 @@ public fun Routing.replaceNamed(
name = name,
parameters = parameters,
routeMethod = StackRouteMethod.Replace,
).tryNeglect(neglect)
).toNeglect(neglect)
)
}

Expand All @@ -108,11 +108,11 @@ public fun Routing.replaceAllNamed(
name = name,
parameters = parameters,
routeMethod = StackRouteMethod.ReplaceAll,
).tryNeglect(neglect)
).toNeglect(neglect)
)
}

private fun ApplicationCall.tryNeglect(neglect: Boolean): ApplicationCall {
stackNeglect = neglect
public fun ApplicationCall.toNeglect(neglect: Boolean): ApplicationCall {
this.neglect = neglect
return this
}
1 change: 1 addition & 0 deletions javascript/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build
18 changes: 18 additions & 0 deletions javascript/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
plugins {
kotlin("multiplatform")
id("org.jlleitschuh.gradle.ktlint")
id("org.jetbrains.kotlinx.kover")
alias(libs.plugins.maven.publish)
}

applyBasicSetup()

kotlin {
sourceSets {
commonMain {
dependencies {
api(projects.core)
}
}
}
}
2 changes: 2 additions & 0 deletions javascript/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
POM_NAME=Javascript
POM_ARTIFACT_ID=javascript
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package dev.programadorthi.routing.javascript

import dev.programadorthi.routing.core.Routing
import dev.programadorthi.routing.core.application
import dev.programadorthi.routing.core.call
import kotlinx.browser.window
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import kotlinx.dom.clear
import org.w3c.dom.Element
import org.w3c.dom.PopStateEvent
import kotlin.js.Json

internal const val METHOD_KEY = "method"
internal const val URI_KEY = "uri"

public fun render(
routing: Routing,
root: Element,
) {
with(routing.application) {
routingFlow = MutableStateFlow(null)

launch {
routingFlow.collect { child ->
if (child != null) {
root.clear()
root.appendChild(child)
}
}
}
}

window.onpopstate = { event ->
onPopState(routing = routing, event = event)
}
}

@Suppress("UNCHECKED_CAST_TO_EXTERNAL_INTERFACE")
private fun onPopState(routing: Routing, event: PopStateEvent) {
val json = event.state as? Json ?: return
val uri = json[URI_KEY] as? String
val method = json[METHOD_KEY] as? String
if (uri.isNullOrBlank() || method.isNullOrBlank()) return
// TODO: add route method and parameters support
routing.call(uri = uri)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package dev.programadorthi.routing.javascript

import dev.programadorthi.routing.core.application.Application
import dev.programadorthi.routing.core.application.ApplicationCall
import io.ktor.util.AttributeKey
import kotlinx.coroutines.flow.MutableStateFlow
import org.w3c.dom.Node

internal val JavascriptRoutingAttributeKey: AttributeKey<MutableStateFlow<Node?>> =
AttributeKey("JavascriptRoutingAttributeKey")

internal var Application.routingFlow: MutableStateFlow<Node?>
get() = attributes[JavascriptRoutingAttributeKey]
internal set(value) {
attributes.put(JavascriptRoutingAttributeKey, value)
}

internal var ApplicationCall.destination: Node?
get() = application.routingFlow.value
internal set(value) {
application.routingFlow.value = value
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package dev.programadorthi.routing.javascript

import dev.programadorthi.routing.core.Route
import dev.programadorthi.routing.core.RouteMethod
import dev.programadorthi.routing.core.application.ApplicationCall
import dev.programadorthi.routing.core.application.call
import dev.programadorthi.routing.core.route
import io.ktor.util.KtorDsl
import io.ktor.util.pipeline.PipelineContext
import kotlinx.browser.window
import org.w3c.dom.Element
import kotlin.js.json

@KtorDsl
public fun Route.jsRoute(
path: String,
name: String? = null,
body: PipelineContext<Unit, ApplicationCall>.() -> Element,
): Route = route(path = path, name = name) { jsRoute(body) }

@KtorDsl
public fun Route.jsRoute(
path: String,
method: RouteMethod,
name: String? = null,
body: PipelineContext<Unit, ApplicationCall>.() -> Element,
): Route = route(path = path, name = name, method = method) { jsRoute(body) }

@KtorDsl
public fun Route.jsRoute(
body: PipelineContext<Unit, ApplicationCall>.() -> Element,
) {
handle {
call.destination = body(this)
val data = json(
METHOD_KEY to call.routeMethod.value,
URI_KEY to call.uri,
)
window.history.pushState(data = data, title = "", url = call.uri)
}
}
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ include(":core")
include(":core-stack")
include(":events")
include(":events-resources")
include(":javascript")
include(":resources")
include(":resources-stack")
include(":sessions")
Expand Down

0 comments on commit 98f9ddf

Please sign in to comment.