2026-04-05 17:12:08 +02:00
|
|
|
import gleam/dynamic/decode
|
2026-03-29 13:53:16 +02:00
|
|
|
import gleam/json
|
2026-03-30 23:49:20 +02:00
|
|
|
import gleam/option.{type Option, None, Some}
|
2026-03-29 13:53:16 +02:00
|
|
|
import gleam/result
|
2026-03-30 23:49:20 +02:00
|
|
|
import gleam/string
|
2026-03-29 13:53:16 +02:00
|
|
|
import lustre
|
|
|
|
|
import lustre/effect.{type Effect}
|
2026-04-06 14:03:33 +02:00
|
|
|
import model.{
|
2026-04-10 19:36:28 +02:00
|
|
|
type Model, type Msg, type Room, Empty, EnterPin, Initialize, KeyPin, Model,
|
|
|
|
|
Room, SelectedGamestyle, SelectedRoom,
|
2026-04-06 14:03:33 +02:00
|
|
|
}
|
2026-03-29 13:53:16 +02:00
|
|
|
import plinth/browser/document
|
|
|
|
|
import plinth/browser/element as plinth_element
|
2026-04-06 14:03:33 +02:00
|
|
|
import view.{view}
|
2026-03-29 13:53:16 +02:00
|
|
|
|
|
|
|
|
pub fn main() {
|
2026-04-10 19:36:28 +02:00
|
|
|
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:))
|
|
|
|
|
}
|
2026-03-29 13:53:16 +02:00
|
|
|
let initial_items =
|
|
|
|
|
document.query_selector("#model")
|
|
|
|
|
|> result.map(plinth_element.inner_text)
|
|
|
|
|
|> result.try(fn(json) {
|
2026-04-10 19:36:28 +02:00
|
|
|
json.parse(json, decode.list(room_decoder))
|
2026-03-29 13:53:16 +02:00
|
|
|
|> result.replace_error(Nil)
|
|
|
|
|
})
|
|
|
|
|
|> result.unwrap([])
|
|
|
|
|
|
|
|
|
|
let app = lustre.application(init, update, view)
|
2026-04-05 17:12:08 +02:00
|
|
|
let assert Ok(_) = lustre.start(app, "#app", #(initial_items, None))
|
2026-03-29 13:53:16 +02:00
|
|
|
|
|
|
|
|
Nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 17:12:08 +02:00
|
|
|
fn init(initial: #(List(Room), Option(String))) -> #(Model, Effect(Msg)) {
|
|
|
|
|
let #(rooms, ohno) = initial
|
|
|
|
|
let model = Model(rooms:, state: Empty, ohno:)
|
2026-03-29 13:53:16 +02:00
|
|
|
|
|
|
|
|
#(model, effect.none())
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 17:12:08 +02:00
|
|
|
fn update(model: Model, msg: Msg) -> #(Model, Effect(Msg)) {
|
2026-03-29 13:53:16 +02:00
|
|
|
case msg {
|
2026-04-05 17:12:08 +02:00
|
|
|
Initialize -> init(#(model.rooms, None))
|
2026-04-06 14:03:33 +02:00
|
|
|
SelectedRoom(room) -> #(
|
2026-04-05 17:12:08 +02:00
|
|
|
Model(..model, state: EnterPin(room:, pin: "")),
|
|
|
|
|
effect.none(),
|
|
|
|
|
)
|
2026-04-06 14:03:33 +02:00
|
|
|
KeyPin(pin) -> {
|
2026-04-05 17:12:08 +02:00
|
|
|
case model.state {
|
2026-04-06 14:03:33 +02:00
|
|
|
EnterPin(room, _) -> #(
|
|
|
|
|
Model(..model, state: case string.length(pin) < 4 {
|
|
|
|
|
False -> model.SelectGamestyle(room:, pin:)
|
|
|
|
|
True -> EnterPin(room:, pin:)
|
|
|
|
|
}),
|
|
|
|
|
effect.none(),
|
|
|
|
|
)
|
2026-04-05 17:12:08 +02:00
|
|
|
_ ->
|
2026-04-06 14:03:33 +02:00
|
|
|
init(#(
|
|
|
|
|
model.rooms,
|
|
|
|
|
Some("(fail: enterpin) Invalid state, starting over"),
|
|
|
|
|
))
|
2026-03-30 23:49:20 +02:00
|
|
|
}
|
2026-04-06 14:03:33 +02:00
|
|
|
}
|
|
|
|
|
SelectedGamestyle(style) -> {
|
|
|
|
|
case model.state {
|
2026-04-10 19:36:28 +02:00
|
|
|
model.SelectGamestyle(room:, pin:) -> {
|
|
|
|
|
#(
|
|
|
|
|
Model(..model, state: case style {
|
|
|
|
|
"Single Game" -> model.JoinSingle(room:, pin:)
|
|
|
|
|
_ -> model.JoinLive(room:, pin:)
|
|
|
|
|
}),
|
|
|
|
|
effect.none(),
|
|
|
|
|
)
|
|
|
|
|
}
|
2026-04-06 14:03:33 +02:00
|
|
|
_ ->
|
|
|
|
|
init(#(
|
|
|
|
|
model.rooms,
|
|
|
|
|
Some("(fail: selectgamestyle) Invalid state, starting over"),
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-05 17:12:08 +02:00
|
|
|
}
|
|
|
|
|
}
|