Skip to content

Commit

Permalink
feat: javascript module easily handling browser history
Browse files Browse the repository at this point in the history
  • Loading branch information
programadorthi committed Jan 15, 2024
1 parent dc83494 commit 15395d3
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 66 deletions.
2 changes: 1 addition & 1 deletion javascript/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ kotlin {
sourceSets {
commonMain {
dependencies {
api(projects.resources)
api(projects.core)
api(libs.serialization.json)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package dev.programadorthi.routing.javascript

import dev.programadorthi.routing.core.RouteMethod
import dev.programadorthi.routing.core.Routing
import dev.programadorthi.routing.core.application
import dev.programadorthi.routing.core.application.ApplicationCall
import io.ktor.http.parametersOf
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

public fun render(
routing: Routing,
Expand All @@ -25,18 +27,24 @@ public fun render(
}
}

// First time or page refresh we try continue from last state
routing.tryNotifyTheRoute(state = window.history.state)

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

private fun onPopState(
routing: Routing,
event: PopStateEvent,
) {
val data = event.state as? String
if (data.isNullOrBlank()) return

val call = data.toCall(routing.application)
routing.execute(call)
private fun Routing.tryNotifyTheRoute(state: Any?) {
val javascriptState = state.deserialize() ?: return
val call =
ApplicationCall(
application = application,
name = javascriptState.name,
uri = javascriptState.uri,
routeMethod = RouteMethod.parse(javascriptState.routeMethod),
parameters = parametersOf(javascriptState.parameters),
)
call.neglect = true
execute(call)
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
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.Element

internal val JavascriptNeglectAttributeKey: AttributeKey<Boolean> =
AttributeKey("JavascriptNeglectAttributeKey")

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

internal var ApplicationCall.neglect: Boolean
get() = attributes.getOrNull(JavascriptNeglectAttributeKey) ?: false
set(value) {
attributes.put(JavascriptNeglectAttributeKey, value)
}

internal var Application.routingFlow: MutableStateFlow<Element>
get() = attributes[JavascriptRoutingAttributeKey]
set(value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,26 @@ public fun Route.jsRoute(body: PipelineContext<Unit, ApplicationCall>.() -> Elem
handle {
application.routingFlow.emit(body(this))

if (call.neglect) {
return@handle
}

when (call.routeMethod) {
RouteMethod.Push ->
window.history.pushState(
data = call.toData(),
title = "",
title = "routing",
url = call.uri,
data = call.serialize(),
)

RouteMethod.Replace ->
window.history.replaceState(
data = call.toData(),
title = "",
title = "routing",
url = call.uri,
data = call.serialize(),
)

else -> TODO("Not implemented yet")
// TODO: Add support to replace all route method
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package dev.programadorthi.routing.javascript
import dev.programadorthi.routing.core.Routing
import kotlinx.browser.window

@Suppress("UnusedReceiverParameter")
public fun Routing.pop() {
window.history.replaceState(data = null, title = "", url = null)
window.history.back()
window.history.go(-1)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package dev.programadorthi.routing.javascript

import dev.programadorthi.routing.core.application.ApplicationCall
import io.ktor.util.toMap
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

@Serializable
internal data class JavascriptRoutingState(
val routeMethod: String,
val name: String,
val uri: String,
val parameters: Map<String, List<String>>,
)

internal fun ApplicationCall.serialize(): String {
val state =
JavascriptRoutingState(
routeMethod = routeMethod.value,
name = name,
uri = uri,
parameters = parameters.toMap(),
)
return Json.encodeToString(state)
}

internal fun Any?.deserialize(): JavascriptRoutingState? =
when (this) {
is String -> Json.decodeFromString(this)
else -> null
}

This file was deleted.

0 comments on commit 15395d3

Please sign in to comment.