new Join Room form that requests authcookie

getting stubbed session #1,
with auth module stubbed to accept that session in.
and stubbed method for streaming room state,
but that's enough to start testing room calling api's for other actions

next - pass in observable from parent to re-toggle subscription
websocket after successful login
This commit is contained in:
efim 2023-04-26 11:42:40 +04:00
parent 1f28a03d47
commit 90e886c62d
6 changed files with 132 additions and 10 deletions

View File

@ -62,7 +62,8 @@ object Auth {
roomService.joinRoom(roomId, nickName, nickPassword, roomPassword)
)
.leftMap(_.toString())
newSessionId = Random.nextLong()
// newSessionId = Random.nextLong() // TODO return after i stop mocking RoomService
newSessionId = TestModels.testSessionId
_ <- EitherT.liftF(sessions.update(_.updated(newSessionId, (roomId, playerId))))
} yield ResponseCookie(
name = authcookieName,

View File

@ -24,10 +24,12 @@ object MyHttpService {
AuthedRoutes.of {
case GET -> Root / "subscribe" as (playerId, roomId) => {
val send: Stream[IO, WebSocketFrame] =
Stream
.emits(TestModels.testChangesList)
.covary[IO]
.metered(1.second)
(
Stream
.emits(TestModels.testChangesList)
.covary[IO]
.metered(1.second) ++ Stream.never[IO]
)
.map(state => WebSocketFrame.Text(state.asJson.noSpaces))
val receive: Pipe[IO, WebSocketFrame, Unit] = _.evalMap {

View File

@ -33,7 +33,7 @@ lazy val frontend = project
libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.4.0",
libraryDependencies += "com.raquo" %%% "laminar" % "15.0.1",
libraryDependencies += "io.laminext" %%% "websocket-circe" % "0.15.0",
libraryDependencies += "io.laminext" %%% "fetch" % "0.15.0"
libraryDependencies += "io.laminext" %%% "fetch-circe" % "0.15.0"
)
.dependsOn(common.js)

View File

@ -3,7 +3,7 @@ package industries.sunshine.planningpoker
import industries.sunshine.planningpoker.common.Models.*
object TestModels {
val me = Player("wormy", PlayerID(1))
val me = Player("me", PlayerID(1))
val pony = Player("pony", PlayerID(10))
val birdy = Player("birdy", PlayerID(11))
val horsey = Player("horsey", PlayerID(12))
@ -17,10 +17,11 @@ object TestModels {
round = RoundState.Viewing(
List(me.id -> "xs", pony.id -> "l", birdy.id -> "s", horsey.id -> "m")
),
playersPasswords = Map.empty // nickname into password
playersPasswords = Map("me" -> "nickpassword") // nickname into password
)
val testSessions = Map(1L -> (testRoomBackend.id, me.id))
val testSessionId = 1L
val testSessions = Map(testSessionId -> (testRoomBackend.id, me.id))
val testRooms = Map(testRoomBackend.id -> testRoomBackend)
val testChangesList = List(

View File

@ -59,7 +59,8 @@ object Main {
className := "h-24 w-full flex flex-for justify-center items-center bg-green-200",
p(className := "text-2xl", "Here be header")
),
RoomView.renderRoom(stateSignal),
child <-- roomStateWSStream.isConnected.map( if (_) emptyNode else JoinRoomComponent.render()),
child <-- roomStateWSStream.isConnected.map( if (_) RoomView.renderRoom(stateSignal) else emptyNode),
roomStateWSStream.connect
)
}

View File

@ -0,0 +1,117 @@
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.circe._
import scala.util.Failure
import scala.util.Success
import concurrent.ExecutionContext.Implicits.global
object JoinRoomComponent {
// TODO inputs for room name, room password, nick name, nick password
// do the get to /login route
// display errors if any.
// but then what? attempt to start the websocket?
// and if websocket closed show this component,
// if it's open - doesn't show this component, show the room.
// i suppose it should be managed on a level up
// or rather what? ws stream should be retried every time someone presses submit button
// and receives 200 ok
// so, parent page should send in observer for the successful auth. and on that observer - start \ restart the websocket
def nameInput(data: Var[String], placeholderText: String) = input(
className := "border-2 m-1 rounded",
placeholder := placeholderText,
controlled(
value <-- data.signal,
onInput.mapToValue --> data.writer
)
)
def passInput(data: Var[String], placeholderText: String) = input(
tpe := "password",
className := "border-2 m-1 rounded",
placeholder := placeholderText,
controlled(
value <-- data.signal,
onInput.mapToValue --> data.writer
)
)
val roomNameVar = Var("testroom")
val roomPassVar = Var("password")
val nicknameVar = Var("me")
val nicknamePass = Var("nickpass")
val (responsesStream, responseReceived) = EventStream.withCallback[FetchResponse[String]]
val submitButton = button(
"Join room",
onClick
.mapTo {
(roomNameVar.now(), roomPassVar.now(), nicknameVar.now(), nicknamePass.now())
}
.flatMap { case (roomName, roomPass, nickname, nicknamePass) =>
Fetch
.post(
"/api/login",
body = Requests.LogIn(
roomName,
nickname,
roomPass,
nicknamePass
)
)
.text
} --> responseReceived
)
def render(): Element = {
div(
className := "flex flex-col h-full justify-center",
"Logging in:",
nameInput(roomNameVar, "Enter room name:"),
passInput(roomPassVar, "room password"),
nameInput(nicknameVar, "Enter your nickname:"),
passInput(nicknamePass, "nickname pass:"),
submitButton,
div(
div(
code("received:")
),
div(
cls := "flex flex-col space-y-4 p-4 max-h-96 overflow-auto bg-gray-900",
children.command <-- responsesStream.recoverToTry.map {
case Success(response) =>
CollectionCommand.Append(
div(
div(
cls := "flex space-x-2 items-center",
code(cls := "text-green-500", "Status: "),
code(cls := "text-green-300", s"${response.status} ${response.statusText}")
),
div(
cls := "text-green-400 text-xs",
code(response.data)
)
)
)
case Failure(exception) =>
CollectionCommand.Append(
div(
div(
cls := "flex space-x-2 items-center",
code(cls := "text-red-500", "Error: "),
code(cls := "text-red-300", exception.getMessage)
)
)
)
}
)
)
)
}
}