import gleam/int import gleam/list import gleam/option.{None, Some} import lustre/attribute.{class} import lustre/element.{type Element} import lustre/element/html import lustre/event import lustre/server_component import model.{ type Model, type Msg, AwaitPlayers, Empty, EnterPin, JoinLive, JoinSingle, KeyPin, PickPlayer, SelectGamestyle, SelectedPlayer, SelectedRoom, } import shared.{type Room} pub fn view(model: Model) -> Element(Msg) { case model.state { Empty -> view_room_list(model.rooms) EnterPin(_, _) -> view_enter_pin() SelectGamestyle(_, _) -> view_live_or_single() AwaitPlayers(_, _) -> html.text("FETCHING USERS FOR ROOM") PickPlayer(_, _, players) -> view_player_list(players) JoinLive(room:, pin:) -> view_join_live(room, pin) JoinSingle(room:, pin:, player:) -> view_join_single(room, pin, player) } } fn layout(header: String, ohno: option.Option(String), body: List(Element(Msg))) { html.div([], [ html.div([class("terminal-header")], [ html.div([class("terminal-status")], [ html.span([class("status-blink")], [html.text("●")]), html.div([], [ html.text(" SYSTEM READY"), ]), html.div([], [ case ohno { None -> element.none() Some(x) -> html.h3([], [html.text("Fail: " <> x)]) }, ]), html.span([class("ml-8")], [ html.text("<< Please Log On to use QuizTerm. >>"), ]), ]), ]), html.div([attribute.class("terminal-section")], [ html.div([attribute.class("terminal-label mb-4")], [ html.text(header), ]), html.div([attribute.class("participants-grid")], body), ]), ]) } fn view_room_list(items: List(Room)) -> Element(Msg) { layout("Select room to play in", None, case items { [] -> [html.text("No items in your list yet.")] _ -> { list.index_map(items, fn(item, index) { room_cell(index, item, SelectedRoom) }) } }) } fn view_enter_pin() -> Element(Msg) { layout("Enter PIN code for room", None, [ html.input([ attribute.type_("password"), event.on_input(KeyPin), attribute.autofocus(True), ]), ]) } fn view_join_live(room: String, pin: String) -> Element(Msg) { html.div([attribute.class("terminal-section")], [ html.div([attribute.class("terminal-label mb-4")], [ server_component.element( [server_component.route("/socket/live/" <> room)], [], ), server_component.element( [server_component.route("/socket/control/" <> room)], [], ), ]), ]) } fn view_join_single(room: String, pin: String, player: String) -> Element(Msg) { html.div([attribute.class("terminal-section")], [ html.div([attribute.class("terminal-label mb-4")], [ server_component.element( [server_component.route("/socket/single/" <> room)], [], ), ]), ]) } fn view_live_or_single() -> Element(Msg) { layout("Select type of play", None, [ click_cell(1, "Live Game", model.SelectedGamestyle), click_cell(2, "Single Game", model.SelectedGamestyle), ]) } fn view_player_list(items: List(String)) -> Element(Msg) { layout("Select or enter your player", None, case items { [] -> [html.text("No items in your list yet.")] _ -> { list.index_map(items, fn(item, index) { click_cell(index, item, SelectedPlayer) }) } }) } fn click_cell( number: Int, player: String, on_click: fn(String) -> msg, ) -> Element(msg) { html.div([class("participant-login"), event.on_click(on_click(player))], [ html.div([class("participant-name")], [ html.text("► " <> "[#" <> int.to_string(number) <> "] " <> player), ]), ]) } fn room_cell( number: Int, room: Room, on_click: fn(String) -> msg, ) -> Element(msg) { html.div([class("participant-login"), event.on_click(on_click(room.id))], [ html.div([class("participant-name")], [ html.text("► " <> "[#" <> int.to_string(number) <> "] Team " <> room.name), ]), ]) }