Compare commits

...

2 Commits

Author SHA1 Message Date
efim ecb1717eb3 front: simple table control buttons 2023-04-27 11:57:20 +04:00
efim f8bf1b961b new logout button
triggers /api/logout and updates app loggedIn state
2023-04-27 10:34:40 +04:00
5 changed files with 86 additions and 10 deletions

View File

@ -28,7 +28,7 @@ object MyHttpService {
Stream
.emits(TestModels.testChangesList)
.covary[IO]
.metered(1.second)
.metered(1.second) ++ Stream.never[IO]
)
.map(state => WebSocketFrame.Text(state.asJson.noSpaces))
@ -48,6 +48,9 @@ object MyHttpService {
case GET -> Root / "new-poll" as (playerId, roomId) => {
IO(println(s">> got request to start new voting from $playerId in $roomId")) >> Ok()
}
case GET -> Root / "logout" as (playerId, roomId) => {
IO(println(s">> got request to logout from $playerId in $roomId")) >> Ok()
}
}
val authMiddleware = AuthMiddleware(auth.authUser)

View File

@ -27,10 +27,7 @@ object Main {
def appElement(): Element = {
div(
className := "w-screen h-screen flex flex-col justify-center items-center",
div(
className := "h-24 w-full flex flex-for justify-center items-center bg-green-200",
p(className := "text-2xl", "Here be header")
),
Header.render(loggedIn.writer),
child <-- loggedIn.signal.map(if (_) emptyNode else JoinRoomComponent.render(loggedIn.writer)),
child <-- loggedIn.signal.map(
if (_) RoomView.renderRoom(loggedIn.writer) else emptyNode

View File

@ -0,0 +1,29 @@
package industries.sunshine.planningpoker
import scala.scalajs.js
import scala.scalajs.js.annotation.*
import com.raquo.laminar.api.L.{*, given}
import io.laminext.fetch.Fetch
import io.laminext.fetch.FetchResponse
import scala.scalajs.js.Dynamic.{global => g}
import concurrent.ExecutionContext.Implicits.global
object Header {
def render(loggedIn: Observer[Boolean]): Element = {
val (responsesStream, responseReceived) = EventStream.withCallback[FetchResponse[String]]
val logoutButton = button(
"Logout",
onClick.flatMap(_ => Fetch.get("/api/logout").text) --> responseReceived,
responsesStream --> Observer(resp => g.console.info(s"${resp.toString()}")),
responsesStream.collect { case resp if resp.ok => false } --> loggedIn
)
div(
className := "h-24 w-full flex flex-for justify-center items-center bg-green-200",
p(className := "text-2xl", "Here be header"),
logoutButton
)
}
}

View File

@ -0,0 +1,41 @@
package industries.sunshine.planningpoker
import scala.scalajs.js
import com.raquo.laminar.api.L.{*, given}
import industries.sunshine.planningpoker.common.Models.RoundState
import com.raquo.airstream.core.Signal
import industries.sunshine.planningpoker.common.Models.RoundState
import industries.sunshine.planningpoker.common.Models.RoundState
import io.laminext.fetch.Fetch
import scala.scalajs.js.Dynamic.{global => g}
import concurrent.ExecutionContext.Implicits.global
object TableControls {
def render(roundSignal: Signal[RoundState]): Element = {
div(
child <-- roundSignal.map {
case RoundState.Viewing(_) => newPollButton()
case RoundState.Voting(_, _) => endPollButton()
}
)
}
val commonButtonStyle = "border-2 border-black rounded-xl m-2 p-1"
def endPollButton(): Element = button(
className := commonButtonStyle,
"end voting",
onClick.flatMap(_ => Fetch.get("/api/end-voting").text) --> Observer(resp =>
g.console.info(resp.toString())
)
)
def newPollButton(): Element = button(
className := commonButtonStyle,
"start next round",
onClick.flatMap(_ => Fetch.get("/api/new-poll").text) --> Observer(resp =>
g.console.info(resp.toString())
)
)
}

View File

@ -11,17 +11,23 @@ object TableView {
// but, can't have map to player, because overall state is required.
// but can i split full state into several observables by that key? i think i should be able to
// so, it's more efficient to share an observable, than to create multiple copies...
def renderTable(roundSignal: Signal[RoomStateView]): Element = {
def renderTable(roomStateSignal: Signal[RoomStateView]): Element = {
val playerIdToCardTypeSignal =
roundSignal
roomStateSignal
.map(state =>
state.players.map(p => p.id -> getPlayerCardType(p.id, state.round, p.name, state.me))
)
div(
className := "w-full h-full border-2 border-amber-700 flex flex-row justify-center items-center bg-green-100",
children <-- playerIdToCardTypeSignal.split(_._1) { (id, initial, cardTypeSignal) =>
renderPlayerCard(cardTypeSignal.map(_._2))
className := "w-full h-full border-2 border-amber-700 flex flex-col justify-center items-center bg-green-100",
div(
className := "w-full border-2 border-amber-600 flex flex-row justify-center items-center bg-green-100",
children <-- playerIdToCardTypeSignal.split(_._1) { (id, initial, cardTypeSignal) =>
renderPlayerCard(cardTypeSignal.map(_._2))
}
),
child <-- roomStateSignal.map { state =>
if (state.canCloseRound) TableControls.render(roomStateSignal.map(_.round)) else emptyNode
}
)
}