Merge pull request 'playerFeedback' (#1) from playerFeedback into master
Reviewed-on: https://git.pheerai.de/pheerai/webbuzzer/pulls/1
This commit is contained in:
commit
45e07d9393
7 changed files with 150 additions and 13 deletions
|
@ -43,3 +43,7 @@ fun HTMLTag.xText(text: String) {
|
||||||
fun HTMLTag.xBind(type: String, data: String) {
|
fun HTMLTag.xBind(type: String, data: String) {
|
||||||
attributes["x-bind:$type"] = data
|
attributes["x-bind:$type"] = data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun HTMLTag.xEffect(effect: String) {
|
||||||
|
attributes["x-effect"] = effect
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,11 @@ fun HTML.createGameMasterDocument() {
|
||||||
rel = "stylesheet",
|
rel = "stylesheet",
|
||||||
type = "text/css"
|
type = "text/css"
|
||||||
)
|
)
|
||||||
|
link(
|
||||||
|
href = "/assets/gamemaster.css",
|
||||||
|
rel = "stylesheet",
|
||||||
|
type = "text/css"
|
||||||
|
)
|
||||||
script {
|
script {
|
||||||
src = "https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"
|
src = "https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"
|
||||||
defer = true
|
defer = true
|
||||||
|
@ -19,27 +24,60 @@ fun HTML.createGameMasterDocument() {
|
||||||
script { src = "/assets/buzzerWebSocket.js" }
|
script { src = "/assets/buzzerWebSocket.js" }
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
xData("{receivedNames: [], buzzerWebSocket: null}")
|
xData("{receivedNames: [], buzzerWebSocket: null, firstPlayer: null }")
|
||||||
xInit(
|
xInit(
|
||||||
//language=JavaScript
|
//language=JavaScript
|
||||||
"""
|
"""
|
||||||
() => {
|
() => {
|
||||||
startWebSocket('/socket/master', (ev) => {
|
startWebSocket('/socket/master', (ev) => {
|
||||||
|
if (receivedNames.length === 0) {
|
||||||
|
firstPlayer = ev.data
|
||||||
|
}
|
||||||
receivedNames.push(ev.data)
|
receivedNames.push(ev.data)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
div(classes = "parent") {
|
xEffect(
|
||||||
button {
|
//language=JavaScript
|
||||||
xOn("click", "receivedNames = []")
|
"""
|
||||||
+"Reset"
|
if (firstPlayer !== null) {
|
||||||
|
Alpine.data.buzzerWebSocket.send(`PLAYER:${"$"}{firstPlayer}`);
|
||||||
|
} else {
|
||||||
|
Alpine.data.buzzerWebSocket.send("CLEAR:");
|
||||||
}
|
}
|
||||||
ol {
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
div(classes = "parent") {
|
||||||
|
div(classes = "player") {
|
||||||
|
xShow("firstPlayer")
|
||||||
|
xText("firstPlayer")
|
||||||
|
}
|
||||||
|
div { xShow("!firstPlayer") }
|
||||||
|
div(classes = "playerlist") list@{
|
||||||
template {
|
template {
|
||||||
attributes["x-for"] = "name in receivedNames"
|
attributes["x-for"] = "name in receivedNames"
|
||||||
this@ol.li { xText("name") }
|
this@list.div(classes = "listedname") { xText("name") }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
button(classes = "submit") {
|
||||||
|
xData("{clicked: false}")
|
||||||
|
xOn(
|
||||||
|
"click",
|
||||||
|
//language=JavaScript
|
||||||
|
"""
|
||||||
|
() => {
|
||||||
|
receivedNames = []
|
||||||
|
firstPlayer = null
|
||||||
|
clicked = true
|
||||||
|
setTimeout(() => {clicked = false}, 50)
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
xOn("mousedown", "clicked = true")
|
||||||
|
xOn("touchstart", "clicked = true")
|
||||||
|
xBind("class", "{'active': clicked}")
|
||||||
|
+"Reset"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,13 +29,23 @@ fun HTML.createPlayerDocument() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
xData("{playerName: \"\", buzzerWebSocket: null}")
|
xData("{playerName: \"\", buzzerWebSocket: null, firstPlayer: null}")
|
||||||
div(classes = "parent") {
|
div(classes = "parent") {
|
||||||
|
xEffect("console.log(firstPlayer)")
|
||||||
xInit(
|
xInit(
|
||||||
//language=JavaScript
|
//language=JavaScript
|
||||||
"""
|
"""
|
||||||
() => {
|
() => {
|
||||||
startWebSocket('/socket/player')
|
startWebSocket('/socket/player', (ev) => {
|
||||||
|
console.log("received: " + ev.data)
|
||||||
|
if (ev.data === "CLEAR:") {
|
||||||
|
firstPlayer = null;
|
||||||
|
} else {
|
||||||
|
let newName = ev.data.split(":")[1]
|
||||||
|
console.log(newName)
|
||||||
|
setTimeout(() => { firstPlayer = newName }, 500)
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
|
@ -54,7 +64,7 @@ fun HTML.createPlayerDocument() {
|
||||||
+"Enter a name first!"
|
+"Enter a name first!"
|
||||||
}
|
}
|
||||||
button(classes = "submit") {
|
button(classes = "submit") {
|
||||||
xShow("playerName")
|
xShow("playerName && !firstPlayer")
|
||||||
xData("{clicked: false}")
|
xData("{clicked: false}")
|
||||||
xOn(
|
xOn(
|
||||||
"click",
|
"click",
|
||||||
|
@ -78,6 +88,11 @@ fun HTML.createPlayerDocument() {
|
||||||
}
|
}
|
||||||
p { +"and I know this!" }
|
p { +"and I know this!" }
|
||||||
}
|
}
|
||||||
|
div(classes = "lockindicator") {
|
||||||
|
xShow("firstPlayer")
|
||||||
|
xBind("class", "{'self': firstPlayer === playerName, 'other': firstPlayer !== playerName }")
|
||||||
|
xText("firstPlayer")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,15 @@ import de.pheerai.buzzer.data.GameMasterSocket
|
||||||
import de.pheerai.buzzer.data.SessionStorage
|
import de.pheerai.buzzer.data.SessionStorage
|
||||||
import io.ktor.http.cio.websocket.*
|
import io.ktor.http.cio.websocket.*
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
|
import org.slf4j.Logger
|
||||||
|
|
||||||
suspend fun WebSocketSession.handleGameMasterSocket() {
|
suspend fun WebSocketSession.handleGameMasterSocket(log: Logger) {
|
||||||
SessionStorage.gameMasterSessions.add(GameMasterSocket(this))
|
SessionStorage.gameMasterSessions.add(GameMasterSocket(this))
|
||||||
for (frame in incoming) {
|
for (frame in incoming) {
|
||||||
when (frame) {
|
when (frame) {
|
||||||
is Frame.Text -> {
|
is Frame.Text -> {
|
||||||
val text = frame.readText()
|
val text = frame.readText()
|
||||||
|
log.info("GM Socket received $text")
|
||||||
SessionStorage.playerSessions.filter { it.session.isActive }
|
SessionStorage.playerSessions.filter { it.session.isActive }
|
||||||
.forEach { it.session.send(text) }
|
.forEach { it.session.send(text) }
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ fun Application.configureRoutes() {
|
||||||
|
|
||||||
private fun Routing.websocketRoutes(log: Logger) {
|
private fun Routing.websocketRoutes(log: Logger) {
|
||||||
webSocket("/socket/player") { handlePlayerSocket(log) }
|
webSocket("/socket/player") { handlePlayerSocket(log) }
|
||||||
webSocket("/socket/master") { handleGameMasterSocket() }
|
webSocket("/socket/master") { handleGameMasterSocket(log) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Routing.clientRoutes() {
|
private fun Routing.clientRoutes() {
|
||||||
|
|
58
src/main/resources/css/gamemaster.css
Normal file
58
src/main/resources/css/gamemaster.css
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
body, html {
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: var(--font-size);
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: var(--bumper-small-size);
|
||||||
|
margin: 0;
|
||||||
|
background-color: var(--background);
|
||||||
|
color: var(--text);
|
||||||
|
}
|
||||||
|
|
||||||
|
div.parent {
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 3fr auto 1fr;
|
||||||
|
height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.player {
|
||||||
|
background-color: var(--foreground02);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
.playerlist {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listedname + .listedname::before {
|
||||||
|
content: "»";
|
||||||
|
padding-left: var(--bumper-small-size);
|
||||||
|
padding-right: var(--bumper-small-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit {
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
font-size: var(--font-size);
|
||||||
|
height: 100%;
|
||||||
|
border: solid 0.25vh var(--highlight-dark);
|
||||||
|
background-color: var(--foreground);
|
||||||
|
color: var(--text);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit.active {
|
||||||
|
background-color: var(--highlight01);
|
||||||
|
}
|
||||||
|
|
|
@ -59,6 +59,26 @@ div > .submit {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
div > button.active {
|
div > .submit.active {
|
||||||
background-color: var(--highlight01);
|
background-color: var(--highlight01);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lockindicator {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: solid 0.25vh var(--highlight-dark);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lockindicator.self {
|
||||||
|
background-color: darkgreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lockindicator.other {
|
||||||
|
background-color: darkred;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue