diff --git a/client/src/view.gleam b/client/src/view.gleam index 75c8ab8..44567c8 100644 --- a/client/src/view.gleam +++ b/client/src/view.gleam @@ -62,18 +62,21 @@ fn view_room_list(items: List(Room)) -> Element(Msg) { fn view_enter_pin() -> Element(Msg) { layout("Enter PIN code for room", None, [ + html.div([class("participant-hidden")], []), + input_cell("[#ENTER PIN]", True, KeyPin), + html.div([class("participant-hidden")], []), ]) } fn view_join_live(room: String, pin: String) -> Element(Msg) { element.fragment([ server_component.element( - [server_component.route("/socket/live/" <> room)], + [server_component.route("/socket/live/" <> room <> "/" <> pin)], [], ), server_component.element( - [server_component.route("/socket/control/" <> room)], + [server_component.route("/socket/control/" <> room <> "/" <> pin)], [], ), ]) @@ -81,7 +84,7 @@ fn view_join_live(room: String, pin: String) -> Element(Msg) { fn view_join_single(room: String, pin: String) -> Element(Msg) { server_component.element( - [server_component.route("/socket/single/" <> room)], + [server_component.route("/socket/single/" <> room <> "/" <> pin)], [], ) } @@ -98,7 +101,7 @@ fn input_cell( password: Bool, on_input: fn(String) -> Msg, ) -> Element(Msg) { - html.div([class("participant-login")], [ + html.div([class("participant-box")], [ html.div([class("participant-name")], [ html.p([], [html.text("► " <> header)]), html.div([], [ diff --git a/server/priv/static/layout.css b/server/priv/static/layout.css index 24b3583..96f9373 100644 --- a/server/priv/static/layout.css +++ b/server/priv/static/layout.css @@ -136,7 +136,7 @@ body { } .participant-hidden { - border: 0px dashed #005500; + border: none; padding: 1rem; color: #000000; transition: all 0.2s; diff --git a/server/src/backend/playerhandler.gleam b/server/src/backend/playerhandler.gleam index a115198..10f51cf 100644 --- a/server/src/backend/playerhandler.gleam +++ b/server/src/backend/playerhandler.gleam @@ -112,16 +112,10 @@ fn add_player(name: String, players: List(#(String, #(String, List(#(_, _)))))) } fn fetch_players( - players: List(#(String, #(String, List(#(_, String))))), - subject: Subject(List(#(String, String))), + players: List(#(String, #(String, List(#(String, String))))), + subject: Subject(List(#(String, #(String, List(#(String, String)))))), ) { - actor.send( - subject, - list.map(players, fn(player) { - let #(id, #(name, _)) = player - #(id, name) - }), - ) + actor.send(subject, players) } // Reschedule a new ping request, and ask clients to ping us back diff --git a/server/src/backend/roomhandler.gleam b/server/src/backend/roomhandler.gleam index 645ab7a..537301a 100644 --- a/server/src/backend/roomhandler.gleam +++ b/server/src/backend/roomhandler.gleam @@ -1,8 +1,11 @@ import backend/playerhandler as player_handler +import gleam/bit_array +import gleam/crypto import gleam/erlang/process.{type Subject} import gleam/list -import gleam/option.{Some} +import gleam/option.{None, Some} import gleam/otp/actor.{type Started} +import gleam/string import group_registry import shared/message.{ type Room, type RoomControl, type StateControl, CreateRoom, FetchRoom, @@ -56,12 +59,22 @@ pub fn initialize(state_handler: Started(Subject(StateControl))) { Ok(_) -> state } } - FetchRoom(id:, subject:) -> { + FetchRoom(id:, pin:, subject:) -> { case // Find the room, if it exists state.rooms |> list.key_find(id) { - Ok(Room(_, _, actors)) -> actor.send(subject, Some(actors)) + Ok(Room(_, pin_enc, actors)) -> { + case + string.uppercase(pin_enc) + == bit_array.base16_encode( + crypto.hash(crypto.Sha256, <>), + ) + { + True -> actor.send(subject, Some(actors)) + False -> actor.send(subject, None) + } + } _ -> actor.send(subject, option.None) } state diff --git a/server/src/backend/sockethandler.gleam b/server/src/backend/sockethandler.gleam index 58db8e7..f4ad8d4 100644 --- a/server/src/backend/sockethandler.gleam +++ b/server/src/backend/sockethandler.gleam @@ -14,9 +14,10 @@ pub fn serve( request: Request(Connection), component: lustre.App(message.ClientsServer, model, msg), id: String, + pin: String, actor: actor.Started(Subject(message.RoomControl)), ) -> Response(ResponseData) { - let start_args = actor.call(actor.data, 1000, message.FetchRoom(id, _)) + let start_args = actor.call(actor.data, 1000, message.FetchRoom(id, pin, _)) case start_args { Some(start_args) -> mist.websocket( @@ -35,13 +36,18 @@ pub fn serve( pub fn serve_slow( request: Request(Connection), - component: lustre.App(#(List(#(String, String)), message.ClientsServer), model, msg), + component: lustre.App( + #(List(#(String, String)), message.ClientsServer), + model, + msg, + ), id: String, + pin: String, roomhandler: actor.Started(Subject(message.RoomControl)), statehandler: actor.Started(Subject(message.StateControl)), ) -> Response(ResponseData) { let start_args_opt = - actor.call(roomhandler.data, 1000, message.FetchRoom(id, _)) + actor.call(roomhandler.data, 1000, message.FetchRoom(id, pin, _)) let answer_list = actor.call(statehandler.data, 1000, message.FetchQuestions) case start_args_opt { diff --git a/server/src/quizterm.gleam b/server/src/quizterm.gleam index f8e4388..62a25c9 100644 --- a/server/src/quizterm.gleam +++ b/server/src/quizterm.gleam @@ -37,15 +37,22 @@ pub fn main() { ["lustre", "runtime.mjs"] -> serve_runtime() ["client.js"] -> serve_static("client.js") ["static", file] -> serve_static(file) - ["socket", "live", id] -> - sockethandler.serve(req, card.component(), id, room_handler) - ["socket", "control", id] -> - sockethandler.serve(req, control.component(), id, room_handler) - ["socket", "single", id] -> + ["socket", "live", id, pin] -> + sockethandler.serve(req, card.component(), id, pin, room_handler) + ["socket", "control", id, pin] -> + sockethandler.serve( + req, + control.component(), + id, + pin, + room_handler, + ) + ["socket", "single", id, pin] -> sockethandler.serve_slow( req, answerlist.component(), id, + pin, room_handler, state_handler, ) diff --git a/server/src/shared/message.gleam b/server/src/shared/message.gleam index dfe84d4..669b94b 100644 --- a/server/src/shared/message.gleam +++ b/server/src/shared/message.gleam @@ -15,7 +15,7 @@ pub type NotifyServer { GiveName(name: String) GiveAnswer(name: String, answer: Option(String)) GiveSingleAnswer(id: String, question: String, answer: String) - FetchPlayers(subject: Subject(List(#(String, String)))) + FetchPlayers(subject: Subject(List(#(String, #(String, List(#(String, String))))))) AddPlayer(String) } @@ -37,7 +37,7 @@ pub type RoomInfo { pub type RoomControl { CreateRoom(id: String, room: RoomInfo) - FetchRoom(id: String, subject: Subject(Option(ClientsServer))) + FetchRoom(id: String, pin: String, subject: Subject(Option(ClientsServer))) FetchRooms(subject: Subject(List(#(String, RoomInfo)))) } diff --git a/server/src/web/components/answerlist.gleam b/server/src/web/components/answerlist.gleam index ebd317d..c59e27f 100644 --- a/server/src/web/components/answerlist.gleam +++ b/server/src/web/components/answerlist.gleam @@ -29,13 +29,24 @@ pub fn component() -> lustre.App( pub opaque type Model { Model( state: Msg, - players: List(#(String, String)), + players: List(#(String, #(String, List(#(String, String))))), player: Option(#(String, String)), answers: List(#(String, #(String, String))), handler: Started(Subject(NotifyServer)), ) } +pub opaque type Msg { + Initial + PickedPlayer(player: Option(#(String, String))) + SharedMessage(message: NotifyClient) + ReceiveName(name: Option(String)) + AcceptPlayer(accept: Option(#(String, String))) + PickQuestion + PickedQuestion(question: Option(#(String, String))) + GiveAnswer(question: #(String, String), answer: Option(String)) +} + fn init( start_args: #(List(#(String, String)), message.ClientsServer), ) -> #(Model, Effect(Msg)) { @@ -63,17 +74,6 @@ fn init( ) } -pub opaque type Msg { - Initial - PickedPlayer(player: Option(#(String, String))) - SharedMessage(message: NotifyClient) - ReceiveName(name: Option(String)) - AcceptPlayer(accept: Option(#(String, String))) - PickQuestion - PickedQuestion(question: Option(#(String, String))) - GiveAnswer(question: #(String, String), answer: Option(String)) -} - fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) { case msg { Initial -> #(Model(..model, state: msg), effect.none()) @@ -168,7 +168,14 @@ fn view(model: Model) -> Element(Msg) { Initial -> case model.players { [] -> shared.input_new_player(ReceiveName) - _ -> shared.view_players(model.players, PickedPlayer) + _ -> + shared.view_players( + list.map(model.players, fn(player) { + let #(id, #(name, _)) = player + #(id, name) + }), + PickedPlayer, + ) } PickQuestion -> view_questions(model.answers) ReceiveName(_) -> shared.input_new_player(ReceiveName) diff --git a/server/src/web/components/card.gleam b/server/src/web/components/card.gleam index 4c0d10e..695eb92 100644 --- a/server/src/web/components/card.gleam +++ b/server/src/web/components/card.gleam @@ -29,7 +29,7 @@ type State { pub opaque type Model { Model( state: State, - players: List(#(String, String)), + players: List(#(String, #(String, List(#(String, String))))), lobby: #(String, List(User)), registry: GroupRegistry(NotifyClient), handler: Started(Subject(NotifyServer)), @@ -137,7 +137,14 @@ fn view(model: Model) -> Element(Msg) { AskName -> case model.players { [] -> input_new_player(ReceiveName) - _ -> view_players(model.players, AcceptName) + _ -> + view_players( + list.map(model.players, fn(player) { + let #(id, #(name, _)) = player + #(id, name) + }), + AcceptName, + ) } NameOk(name) -> { shared.confirm_cells(