fix webmap after multi-server refactor, and add styling
This commit is contained in:
parent
8a6c49a74b
commit
3976c3cc1b
14 changed files with 12461 additions and 96 deletions
|
|
@ -1490,7 +1490,7 @@ http-parser-js@>=0.5.1:
|
|||
resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3"
|
||||
integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==
|
||||
|
||||
"http-proxy-middleware@>=2.0.6 <3.0.0", http-proxy-middleware@^2.0.3, http-proxy-middleware@^2.0.6:
|
||||
http-proxy-middleware@^2.0.3:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f"
|
||||
integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==
|
||||
|
|
|
|||
|
|
@ -158,3 +158,18 @@ val dockerEnvUpdate by tasks.registering(DockerEnvUpdateTask::class) {
|
|||
)
|
||||
}
|
||||
tasks.assemble { dependsOn(dockerEnvUpdate) }
|
||||
|
||||
|
||||
val installThemeCss by tasks.registering(Copy::class) {
|
||||
// val bootswatchThemeCss = resources.text.fromUri("https://bootswatch.com/5/darkly/bootstrap.css")
|
||||
val bootswatchThemeMinCss =
|
||||
resources.text.fromUri("https://bootswatch.com/5/darkly/bootstrap.min.css")
|
||||
from(bootswatchThemeMinCss) {
|
||||
rename { "bootstrap.min.css" }
|
||||
}
|
||||
into(layout.projectDirectory.dir("src/main/resources/css"))
|
||||
}
|
||||
|
||||
tasks.assemble {
|
||||
dependsOn(installThemeCss)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,16 +3,16 @@ package dev.adamko.kafkatorio.webmap
|
|||
import dev.adamko.kafkatorio.schema.packets.EventServerPacket
|
||||
import dev.adamko.kafkatorio.webmap.layout.headerNav
|
||||
import dev.adamko.kafkatorio.webmap.layout.homePage
|
||||
import dev.adamko.kafkatorio.webmap.layout.serverPage
|
||||
import dev.adamko.kafkatorio.webmap.layout.servers.serverPage
|
||||
import dev.adamko.kafkatorio.webmap.services.WebsocketService
|
||||
import io.kvision.Application
|
||||
import io.kvision.BootstrapCssModule
|
||||
import io.kvision.BootstrapIconsModule
|
||||
import io.kvision.BootstrapModule
|
||||
import io.kvision.ChartModule
|
||||
import io.kvision.CoreModule
|
||||
import io.kvision.FontAwesomeModule
|
||||
import io.kvision.html.div
|
||||
import io.kvision.html.h3
|
||||
import io.kvision.html.header
|
||||
import io.kvision.html.main
|
||||
import io.kvision.module
|
||||
|
|
@ -38,7 +38,7 @@ import kotlinx.coroutines.flow.onEach
|
|||
val rootJob = Job()
|
||||
|
||||
|
||||
class App : Application() {
|
||||
object App : Application() {
|
||||
|
||||
private val coroutineScope: CoroutineScope = CoroutineScope(
|
||||
Dispatchers.Default
|
||||
|
|
@ -48,15 +48,23 @@ class App : Application() {
|
|||
|
||||
private var appState: MutableMap<String, Any> = mutableMapOf()
|
||||
|
||||
var reduxStore: ReduxStore<SiteState, SiteAction> by appState
|
||||
var siteStateStore: ReduxStore<SiteState, SiteAction> by appState
|
||||
private set
|
||||
// var wsService: WebsocketService by appState
|
||||
// private set
|
||||
|
||||
init {
|
||||
this.reduxStore = createReduxStore(SiteState::plus, SiteState())
|
||||
this.siteStateStore = createReduxStore(SiteState::plus, SiteState())
|
||||
// this.wsService = wsService
|
||||
require("./css/kafkatorio.css")
|
||||
|
||||
WebsocketService.packetsFlow
|
||||
.filterIsInstance<EventServerPacket.ChunkTileSaved>()
|
||||
.onEach { tileSavedEvent ->
|
||||
// println("[packetsFlow] triggering tile refresh ${tileSavedEvent.filename.value}")
|
||||
siteStateStore.dispatch(SiteAction.EventServerUpdate(tileSavedEvent))
|
||||
// reduxStore.map.refreshUpdatedTilePng(tileSavedEvent.filename)
|
||||
}.launchIn(coroutineScope)
|
||||
}
|
||||
|
||||
// private val gameState: FactorioGameState
|
||||
|
|
@ -70,25 +78,27 @@ class App : Application() {
|
|||
|
||||
this.appState = state.toMutableMap()
|
||||
|
||||
val siteRouting = SiteRouting(reduxStore)
|
||||
val websocketService = WebsocketService(reduxStore)
|
||||
// val siteRouting = SiteRouting(reduxStore)
|
||||
// val websocketService = WebsocketService(siteStateStore)
|
||||
|
||||
siteRouting.init()
|
||||
SiteRouting.init()
|
||||
|
||||
root("kvapp") {
|
||||
|
||||
header().bind(siteRouting.siteStateStore) { state ->
|
||||
header().bind(siteStateStore) { state ->
|
||||
headerNav(state)
|
||||
}
|
||||
|
||||
div("Kafkatorio Web Map")
|
||||
h3("Kafkatorio Web Map")
|
||||
|
||||
main().bind(siteRouting.siteStateStore) { state ->
|
||||
main().bind(siteStateStore) { state ->
|
||||
div(className = "container-fluid") {
|
||||
when (state.view) {
|
||||
SiteView.HOME -> homePage(state)
|
||||
SiteView.SERVER -> serverPage(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// div("Kafkatorio Web Map")
|
||||
// add(kvMaps)
|
||||
|
|
@ -103,14 +113,6 @@ class App : Application() {
|
|||
// })
|
||||
// }
|
||||
|
||||
websocketService.packetsFlow
|
||||
.filterIsInstance<EventServerPacket.ChunkTileSaved>()
|
||||
.onEach { tileSavedEvent ->
|
||||
// println("[packetsFlow] triggering tile refresh ${tileSavedEvent.filename.value}")
|
||||
reduxStore.dispatch(SiteAction.EventServerUpdate(tileSavedEvent))
|
||||
// reduxStore.map.refreshUpdatedTilePng(tileSavedEvent.filename)
|
||||
}.launchIn(coroutineScope)
|
||||
|
||||
}
|
||||
|
||||
override fun dispose(): Map<String, Any> {
|
||||
|
|
@ -129,14 +131,16 @@ fun main() {
|
|||
// val wsService = WebsocketService(
|
||||
// reduxStore = reduxStore,
|
||||
// )
|
||||
App(
|
||||
// App(
|
||||
// reduxStore = reduxStore,
|
||||
// wsService = wsService,
|
||||
)
|
||||
// )
|
||||
|
||||
App
|
||||
},
|
||||
module.hot,
|
||||
BootstrapModule,
|
||||
BootstrapCssModule,
|
||||
// BootstrapCssModule,
|
||||
BootstrapIconsModule,
|
||||
FontAwesomeModule,
|
||||
ChartModule,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
package dev.adamko.kafkatorio.webmap
|
||||
package dev.adamko.kafkatorio.webmap.config
|
||||
|
||||
import kotlinx.browser.window
|
||||
|
||||
object Props {
|
||||
|
||||
object ApplicationProperties {
|
||||
@Suppress("HttpUrlsUsage")
|
||||
val websocketServerUrl: String
|
||||
get() = window.location.href
|
||||
get() = window.location.origin
|
||||
.replace("http://", "ws://")
|
||||
.replace("https://", "wss://") +
|
||||
"kafkatorio/ws"
|
||||
"/kafkatorio/ws"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import io.kvision.html.nav
|
|||
fun Container.headerNav(state: SiteState) {
|
||||
nav(className = "navbar") {
|
||||
div(className = "container") {
|
||||
link("home", "#${SiteView.HOME.pathMatcher}")
|
||||
link("home", SiteView.HOME.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
package dev.adamko.kafkatorio.webmap.layout
|
||||
|
||||
import dev.adamko.kafkatorio.webmap.SiteRouting
|
||||
import dev.adamko.kafkatorio.webmap.SiteState
|
||||
import io.kvision.core.Container
|
||||
import io.kvision.html.ButtonSize
|
||||
import io.kvision.html.ButtonStyle
|
||||
import io.kvision.html.button
|
||||
import io.kvision.html.div
|
||||
import io.kvision.html.h5
|
||||
import io.kvision.html.link
|
||||
import io.kvision.html.span
|
||||
|
||||
|
||||
fun Container.homePage(state: SiteState) {
|
||||
|
|
@ -18,9 +22,32 @@ private fun Container.servers(state: SiteState) {
|
|||
div(className = "servers") {
|
||||
h5("Servers")
|
||||
if (state.serverIds != null) {
|
||||
state.serverIds.forEach { serverId ->
|
||||
div {
|
||||
link("server", "/servers/$serverId")
|
||||
span("Available servers: ${state.serverIds.size}")
|
||||
}
|
||||
|
||||
div {
|
||||
state.serverIds.forEach { serverId ->
|
||||
// div {
|
||||
// link(
|
||||
// label = "$serverId",
|
||||
// dataNavigo = true,
|
||||
// url = "/servers/$serverId",
|
||||
// ) {
|
||||
//// onClick {
|
||||
//// SiteRouting.serverView(serverId)
|
||||
//// }
|
||||
// }
|
||||
// }
|
||||
|
||||
button(text = "$serverId") {
|
||||
style = ButtonStyle.PRIMARY
|
||||
size = ButtonSize.SMALL
|
||||
|
||||
onClick {
|
||||
SiteRouting.serverView(serverId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package dev.adamko.kafkatorio.webmap.layout
|
||||
package dev.adamko.kafkatorio.webmap.layout.servers
|
||||
|
||||
import dev.adamko.kafkatorio.webmap.SiteState
|
||||
import io.kvision.core.Container
|
||||
|
|
@ -8,19 +8,23 @@ import io.kvision.maps.externals.leaflet.layer.tile.GridLayer
|
|||
|
||||
fun Container.serverPage(state: SiteState) {
|
||||
val factorioGameState = state.factorioGameState
|
||||
|
||||
div(className = "server-page") {
|
||||
if (factorioGameState != null) {
|
||||
|
||||
add(factorioGameState.map.kvMap)
|
||||
}
|
||||
}
|
||||
|
||||
factorioGameState.map.kvMap.leafletMap {
|
||||
invalidateSize(true)
|
||||
eachLayer({
|
||||
if (it is GridLayer<*>) {
|
||||
it.redraw()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO figure out how to run this after adding kvMap to the container...
|
||||
// (doing it now causes an NPE because kvMap isn't added to the DOM yet
|
||||
// factorioGameState?.map?.kvMap?.leafletMap {
|
||||
// invalidateSize(true)
|
||||
// eachLayer({
|
||||
// if (it is GridLayer<*>) {
|
||||
// it.redraw()
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
|
@ -37,8 +37,10 @@ class FactorioMap(
|
|||
) {
|
||||
|
||||
val kvMap: Maps = Maps {
|
||||
width = 800.px
|
||||
height = 800.px
|
||||
minHeight = 800.px
|
||||
minWidth = 800.px
|
||||
// width = 100.vh
|
||||
// height = 100.vh
|
||||
margin = 10.px
|
||||
}
|
||||
|
||||
|
|
@ -164,12 +166,5 @@ class FactorioMap(
|
|||
|
||||
companion object {
|
||||
const val DYNAMIC_RELOAD_ATT = "dynamic-reload"
|
||||
|
||||
// private val serverName: String?
|
||||
// get() = window.location.pathname
|
||||
// .substringAfter("servers")
|
||||
// .substringBefore("/")
|
||||
// .ifBlank { null }
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,40 +9,44 @@ import dev.adamko.kafkatorio.webmap.state.FactorioGameState
|
|||
import io.kvision.navigo.Match
|
||||
import io.kvision.navigo.Navigo
|
||||
import io.kvision.redux.RAction
|
||||
import io.kvision.redux.ReduxStore
|
||||
import io.kvision.routing.routing
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.js.RegExp
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class SiteRouting(
|
||||
val siteStateStore: ReduxStore<SiteState, SiteAction>
|
||||
) : CoroutineScope by CoroutineScope(Dispatchers.Default + SupervisorJob()) {
|
||||
object SiteRouting : CoroutineScope {
|
||||
|
||||
override val coroutineContext: CoroutineContext =
|
||||
Dispatchers.Default + SupervisorJob(rootJob) + CoroutineName("SiteRouting")
|
||||
|
||||
|
||||
fun init() {
|
||||
routing
|
||||
.on(SiteView.HOME) { homepageView() }
|
||||
.on(SiteView.SERVER) { match ->
|
||||
serverView(match)
|
||||
val serverId = match.data[0] as? String ?: error("invalid server id")
|
||||
serverView(FactorioServerId(serverId))
|
||||
}
|
||||
.resolve()
|
||||
}
|
||||
|
||||
private fun homepageView() {
|
||||
siteStateStore.dispatch(SiteAction.HomePage)
|
||||
fun homepageView() {
|
||||
App.siteStateStore.dispatch(SiteAction.HomePage)
|
||||
launch {
|
||||
val serverIds = EventsServerClient.serverIds()
|
||||
siteStateStore.dispatch(SiteAction.ServerIdsLoaded(serverIds))
|
||||
App.siteStateStore.dispatch(SiteAction.ServerIdsLoaded(serverIds))
|
||||
}
|
||||
}
|
||||
|
||||
private fun serverView(match: Match) {
|
||||
val serverId = match.data[0] as? String ?: error("invalid server id")
|
||||
siteStateStore.dispatch(SiteAction.ServerPage(FactorioServerId(serverId)))
|
||||
fun serverView(serverId: FactorioServerId) {
|
||||
App.siteStateStore.dispatch(SiteAction.ServerPage(serverId))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -52,7 +56,10 @@ data class SiteState(
|
|||
val serverIds: List<FactorioServerId>? = null,
|
||||
|
||||
val factorioGameState: FactorioGameState? = null,
|
||||
) {
|
||||
) : CoroutineScope {
|
||||
|
||||
override val coroutineContext: CoroutineContext =
|
||||
Dispatchers.Default + SupervisorJob(rootJob) + CoroutineName("SiteState")
|
||||
|
||||
operator fun plus(action: SiteAction): SiteState = when (action) {
|
||||
is SiteAction.HomePage -> copy(view = SiteView.HOME)
|
||||
|
|
@ -68,9 +75,20 @@ data class SiteState(
|
|||
factorioGameState = factorioGameState?.let { it + action }
|
||||
)
|
||||
|
||||
is SiteAction.EventServerUpdate -> copy(
|
||||
is SiteAction.EventServerUpdate -> when (action.packet) {
|
||||
is EventServerPacket.ChunkTileSaved -> {
|
||||
if (factorioGameState?.map != null) {
|
||||
launch {
|
||||
factorioGameState.map.refreshUpdatedTilePng(action.packet.filename)
|
||||
}
|
||||
}
|
||||
this
|
||||
}
|
||||
|
||||
)
|
||||
is EventServerPacket.Kafkatorio -> {
|
||||
this
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -93,19 +111,18 @@ sealed interface SiteAction : RAction {
|
|||
|
||||
|
||||
sealed interface SiteView {
|
||||
val pathMatcher: Any
|
||||
|
||||
sealed interface StaticPath : SiteView {
|
||||
override val pathMatcher: String
|
||||
val path: String
|
||||
}
|
||||
|
||||
sealed interface DynamicPath : SiteView {
|
||||
override val pathMatcher: RegExp
|
||||
val pathMatcher: RegExp
|
||||
}
|
||||
|
||||
|
||||
object HOME : StaticPath {
|
||||
override val pathMatcher: String = "/"
|
||||
override val path: String = "/"
|
||||
}
|
||||
|
||||
object SERVER : DynamicPath {
|
||||
|
|
@ -113,19 +130,15 @@ sealed interface SiteView {
|
|||
}
|
||||
}
|
||||
|
||||
//val SiteView.urlMatcher: String
|
||||
// get() = when (this) {
|
||||
// SiteView.HOME -> "/"
|
||||
// SiteView.SERVER -> "servers/${FactorioServerId.validIdRegex}"
|
||||
// }
|
||||
|
||||
private fun Navigo.on(view: SiteView, handler: (Match) -> Unit): Navigo {
|
||||
return when (view) {
|
||||
is SiteView.StaticPath -> on(view.pathMatcher, { match: Match -> handler(match) })
|
||||
is SiteView.StaticPath -> on(view.path, { match: Match -> handler(match) })
|
||||
is SiteView.DynamicPath -> on(view.pathMatcher, { match: Match -> handler(match) })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//private fun Navigo.on(
|
||||
// view: SiteView,
|
||||
// before: BeforeHook? = null,
|
||||
|
|
|
|||
|
|
@ -12,12 +12,11 @@ import dev.adamko.kafkatorio.schema.packets.MapChunkTileUpdate
|
|||
import dev.adamko.kafkatorio.schema.packets.PlayerUpdate
|
||||
import dev.adamko.kafkatorio.schema.packets.PrototypesUpdate
|
||||
import dev.adamko.kafkatorio.schema.packets.SurfaceUpdate
|
||||
import dev.adamko.kafkatorio.webmap.App
|
||||
import dev.adamko.kafkatorio.webmap.SiteAction
|
||||
import dev.adamko.kafkatorio.webmap.SiteState
|
||||
import dev.adamko.kafkatorio.webmap.config.ApplicationProperties
|
||||
import dev.adamko.kafkatorio.webmap.config.jsonMapper
|
||||
import dev.adamko.kafkatorio.webmap.rootJob
|
||||
import io.kvision.redux.ReduxStore
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
@ -31,9 +30,7 @@ import org.w3c.dom.WebSocket
|
|||
import org.w3c.dom.events.Event
|
||||
|
||||
|
||||
class WebsocketService(
|
||||
private val siteStateStore: ReduxStore<SiteState, SiteAction>
|
||||
) : CoroutineScope {
|
||||
object WebsocketService : CoroutineScope {
|
||||
|
||||
override val coroutineContext: CoroutineContext =
|
||||
CoroutineName("WebsocketService") + Job(rootJob)
|
||||
|
|
@ -80,7 +77,7 @@ class WebsocketService(
|
|||
is EventServerPacket.Kafkatorio ->
|
||||
when (val packetData = packet.packet.data) {
|
||||
is PlayerUpdate ->
|
||||
siteStateStore.dispatch(
|
||||
App.siteStateStore.dispatch(
|
||||
SiteAction.FactorioUpdate.Player(packet.packet.tick, packetData)
|
||||
)
|
||||
|
||||
|
|
|
|||
12303
modules/web-map/src/main/resources/css/bootstrap.min.css
vendored
Normal file
12303
modules/web-map/src/main/resources/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -7,14 +7,16 @@
|
|||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
html {
|
||||
background: #2B2B2BFF;
|
||||
}
|
||||
/*html {*/
|
||||
/* background: #2B2B2BFF;*/
|
||||
/*}*/
|
||||
|
||||
body {
|
||||
background: #2B2B2BFF;
|
||||
}
|
||||
/*body {*/
|
||||
/* background: #2B2B2BFF;*/
|
||||
/*}*/
|
||||
|
||||
.leaflet-container {
|
||||
background-color: rgba(255, 0, 0, 0.0);
|
||||
background-color: rgba(255, 0, 0, 0.0) !important;
|
||||
/*background: rgb(34, 34, 34) !important;*/
|
||||
/*background: var(--bs-body-bg-rgb) !important;*/
|
||||
}
|
||||
|
|
|
|||
BIN
modules/web-map/src/main/resources/img/kafkatorio-logo.png
Normal file
BIN
modules/web-map/src/main/resources/img/kafkatorio-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
|
|
@ -4,7 +4,11 @@
|
|||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Kafkatorio Web Map</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@5.1.0/dist/superhero/bootstrap.min.css" integrity="sha256-T8h7oW5tHp4MhFhmRoDI8xjnB2ld89qSgc3s9g8T0uU=" crossorigin="anonymous">
|
||||
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@5.1.0/dist/superhero/bootstrap.min.css" integrity="sha256-T8h7oW5tHp4MhFhmRoDI8xjnB2ld89qSgc3s9g8T0uU=" crossorigin="anonymous">-->
|
||||
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="/css/kafkatorio.css">
|
||||
<link rel="icon" type="image/png" href="/img/kafkatorio-logo.png"/>
|
||||
<!--suppress HtmlUnknownTarget -->
|
||||
<script type="text/javascript" src="main.bundle.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue