More massive workstuff for completion :P
This commit is contained in:
parent
7a8acf27a7
commit
3385118b14
20 changed files with 325 additions and 354 deletions
|
|
@ -1,27 +1,30 @@
|
|||
import gleam/dynamic/decode
|
||||
import gleam/json
|
||||
import gleam/list
|
||||
import gleam/option.{type Option, None, Some}
|
||||
import gleam/result
|
||||
import gleam/string
|
||||
import lustre
|
||||
import lustre/effect.{type Effect}
|
||||
import model.{
|
||||
type Model, type Msg, AwaitPlayers, Empty, EnterPin, Initialize, KeyPin, Model,
|
||||
PickPlayer, Players, SelectedGamestyle, SelectedPlayer, SelectedRoom,
|
||||
type Model, type Msg, type Room, Empty, EnterPin, Initialize, KeyPin, Model,
|
||||
Room, SelectedGamestyle, SelectedRoom,
|
||||
}
|
||||
import plinth/browser/document
|
||||
import plinth/browser/element as plinth_element
|
||||
import rsvp
|
||||
import shared.{type Room}
|
||||
import view.{view}
|
||||
|
||||
pub fn main() {
|
||||
let room_decoder = {
|
||||
use name <- decode.field("name", decode.string)
|
||||
use id <- decode.field("id", decode.string)
|
||||
use pin <- decode.field("key", decode.string)
|
||||
decode.success(Room(id:, name:, pin:))
|
||||
}
|
||||
let initial_items =
|
||||
document.query_selector("#model")
|
||||
|> result.map(plinth_element.inner_text)
|
||||
|> result.try(fn(json) {
|
||||
json.parse(json, shared.grocery_list_decoder())
|
||||
json.parse(json, decode.list(room_decoder))
|
||||
|> result.replace_error(Nil)
|
||||
})
|
||||
|> result.unwrap([])
|
||||
|
|
@ -47,14 +50,6 @@ fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) {
|
|||
effect.none(),
|
||||
)
|
||||
KeyPin(pin) -> {
|
||||
//let _decode_answer = {
|
||||
// use id <- decode.field("id", decode.string)
|
||||
// use text <- decode.field("text", decode.string)
|
||||
// decode.success(id)
|
||||
// }
|
||||
// let decode_list = {
|
||||
// decode.list(decode.string)
|
||||
// }
|
||||
case model.state {
|
||||
EnterPin(room, _) -> #(
|
||||
Model(..model, state: case string.length(pin) < 4 {
|
||||
|
|
@ -72,97 +67,21 @@ fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) {
|
|||
}
|
||||
SelectedGamestyle(style) -> {
|
||||
case model.state {
|
||||
model.SelectGamestyle(room:, pin:) -> #(
|
||||
Model(..model, state: case style {
|
||||
"Single Game" ->
|
||||
PickPlayer(room:, pin:, players: [
|
||||
"Player a",
|
||||
"Player b",
|
||||
"Player c",
|
||||
"Player d",
|
||||
"Player e",
|
||||
"Player f",
|
||||
])
|
||||
_ -> model.JoinLive(room:, pin:)
|
||||
}),
|
||||
effect.none(),
|
||||
)
|
||||
model.SelectGamestyle(room:, pin:) -> {
|
||||
#(
|
||||
Model(..model, state: case style {
|
||||
"Single Game" -> model.JoinSingle(room:, pin:)
|
||||
_ -> model.JoinLive(room:, pin:)
|
||||
}),
|
||||
effect.none(),
|
||||
)
|
||||
}
|
||||
_ ->
|
||||
init(#(
|
||||
model.rooms,
|
||||
Some("(fail: selectgamestyle) Invalid state, starting over"),
|
||||
))
|
||||
}
|
||||
// Model(..model, state: AwaitPlayers(room:, pin:)),
|
||||
// rsvp.post(
|
||||
// "http://localhost:1234/api/room/players",
|
||||
// json.object([#("id", json.string("1234"))]),
|
||||
// rsvp.expect_json(decode_answer, Players),
|
||||
// ),
|
||||
// )
|
||||
}
|
||||
Players(Ok(players)) -> {
|
||||
echo "got players"
|
||||
case model.state {
|
||||
AwaitPlayers(room:, pin:) -> #(
|
||||
Model(..model, state: PickPlayer(room:, pin:, players: [players])),
|
||||
effect.none(),
|
||||
)
|
||||
_ ->
|
||||
init(#(
|
||||
model.rooms,
|
||||
Some("(fail: awaitplayers) Invalid state, starting over"),
|
||||
))
|
||||
}
|
||||
}
|
||||
Players(Error(x)) ->
|
||||
init(#(
|
||||
model.rooms,
|
||||
Some("Error fetching players " <> decode_rsvp_error(x)),
|
||||
))
|
||||
SelectedPlayer(player) ->
|
||||
case model.state {
|
||||
PickPlayer(room:, pin:, players: _) -> #(
|
||||
Model(..model, state: model.JoinSingle(room:, pin:, player:)),
|
||||
effect.none(),
|
||||
)
|
||||
_ ->
|
||||
init(#(
|
||||
model.rooms,
|
||||
Some("(fail: pickplayer) Invalid state, starting over"),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_rsvp_error(rsvp_error: rsvp.Error) {
|
||||
case rsvp_error {
|
||||
rsvp.BadBody -> "Bad body"
|
||||
rsvp.BadUrl(_x) -> "Bad url"
|
||||
rsvp.HttpError(_x) -> "Http error"
|
||||
rsvp.JsonError(x) -> "Json error" <> decode_decode_error(x)
|
||||
rsvp.NetworkError -> "Network error"
|
||||
rsvp.UnhandledResponse(_) -> "Unhandled response"
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_decode_error(json_error: json.DecodeError) -> String {
|
||||
case json_error {
|
||||
json.UnableToDecode(x) ->
|
||||
"[unable to decode"
|
||||
<> string.concat(
|
||||
list.map(x, fn(x) { "(" <> decode_ddecode_error(x) <> ")" }),
|
||||
)
|
||||
<> "]"
|
||||
json.UnexpectedSequence(s) -> "[unexpected sequence " <> s <> "]"
|
||||
json.UnexpectedByte(x) -> "[unexpected byte " <> x <> "]"
|
||||
json.UnexpectedEndOfInput -> "[unexpected end of input]"
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_ddecode_error(decode_error: decode.DecodeError) -> String {
|
||||
case decode_error {
|
||||
decode.DecodeError(expected:, found:, path:) ->
|
||||
"(e: " <> expected <> ", f: " <> found <> ", p: " <> string.concat(path)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import gleam/option.{type Option}
|
||||
import rsvp.{type Error}
|
||||
import shared.{type Room}
|
||||
|
||||
pub type Model {
|
||||
Model(rooms: List(Room), state: State, ohno: Option(String))
|
||||
|
|
@ -10,17 +9,17 @@ pub type State {
|
|||
Empty
|
||||
EnterPin(room: String, pin: String)
|
||||
SelectGamestyle(room: String, pin: String)
|
||||
AwaitPlayers(room: String, pin: String)
|
||||
PickPlayer(room: String, pin: String, players: List(String))
|
||||
JoinLive(room: String, pin: String)
|
||||
JoinSingle(room: String, pin: String, player: String)
|
||||
JoinSingle(room: String, pin: String)
|
||||
}
|
||||
|
||||
pub type Msg {
|
||||
Initialize
|
||||
SelectedRoom(String)
|
||||
SelectedPlayer(String)
|
||||
SelectedGamestyle(String)
|
||||
KeyPin(String)
|
||||
Players(Result(String, Error))
|
||||
}
|
||||
|
||||
pub type Room {
|
||||
Room(id: String, name: String, pin: String)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
import gleam/dynamic/decode
|
||||
|
||||
pub type Room {
|
||||
Room(id: String, name: String, pin: String)
|
||||
}
|
||||
|
||||
|
||||
pub fn grocery_list_decoder() -> decode.Decoder(List(Room)) {
|
||||
let room_decoder = {
|
||||
use name <- decode.field("name", decode.string)
|
||||
use id <- decode.field("id", decode.string)
|
||||
use pin <- decode.field("key", decode.string)
|
||||
decode.success(Room(id:, name:, pin: ))
|
||||
}
|
||||
decode.list(room_decoder)
|
||||
}
|
||||
|
|
@ -7,20 +7,17 @@ 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,
|
||||
type Model, type Msg, type Room, Empty, EnterPin, JoinLive, JoinSingle, KeyPin,
|
||||
SelectGamestyle, 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)
|
||||
JoinSingle(room:, pin:) -> view_join_single(room, pin)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -65,38 +62,28 @@ fn view_room_list(items: List(Room)) -> Element(Msg) {
|
|||
|
||||
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),
|
||||
]),
|
||||
input_cell("[#ENTER PIN]", True, KeyPin),
|
||||
])
|
||||
}
|
||||
|
||||
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)],
|
||||
[],
|
||||
),
|
||||
]),
|
||||
element.fragment([
|
||||
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_join_single(room: String, pin: String) -> Element(Msg) {
|
||||
server_component.element(
|
||||
[server_component.route("/socket/single/" <> room)],
|
||||
[],
|
||||
)
|
||||
}
|
||||
|
||||
fn view_live_or_single() -> Element(Msg) {
|
||||
|
|
@ -106,15 +93,27 @@ fn view_live_or_single() -> Element(Msg) {
|
|||
])
|
||||
}
|
||||
|
||||
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 input_cell(
|
||||
header: String,
|
||||
password: Bool,
|
||||
on_input: fn(String) -> Msg,
|
||||
) -> Element(Msg) {
|
||||
html.div([class("participant-login")], [
|
||||
html.div([class("participant-name")], [
|
||||
html.text("► " <> header),
|
||||
html.div([], [
|
||||
html.input([
|
||||
attribute.type_(case password {
|
||||
True -> "password"
|
||||
False -> "text"
|
||||
}),
|
||||
event.on_input(on_input),
|
||||
attribute.autofocus(True),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
])
|
||||
}
|
||||
|
||||
fn click_cell(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue