Handle presence events in Elm

Pass state and diff events to elm, where the state can be managed
accordingly. Because vote events are used to update player state, we
don't want to blindly overwrite it by passing a separately synced
presence list through, as it will lack vote data.

Fixes #1
This commit is contained in:
Correl Roush 2020-05-20 21:24:01 -04:00
parent 4e8ac6f8d2
commit e5777f6f7a
3 changed files with 74 additions and 26 deletions

View file

@ -38,17 +38,8 @@ app.ports.joinRoom.subscribe(options => {
let channel = socket.channel("room:" + options.room, {})
// Presence events
let presences = {}
channel.on("presence_state", state => {
console.log("presence state", state)
presences = Presence.syncState(presences, state)
app.ports.gotPresence.send(presences)
})
channel.on("presence_diff", diff => {
console.log("presence diff", diff)
presences = Presence.syncDiff(presences, diff)
app.ports.gotPresence.send(presences)
})
channel.on("presence_state", app.ports.gotPresenceState.send)
channel.on("presence_diff", app.ports.gotPresenceDiff.send)
// Incoming room events
channel.on("vote", app.ports.gotVote.send)

View file

@ -1,5 +1,6 @@
port module PlanningPokerAPI exposing
( gotPresence
( gotPresenceDiff
, gotPresenceState
, gotReset
, gotReveal
, gotVote
@ -73,7 +74,10 @@ encodeAction action =
wrap "reveal" (Encode.object [])
port gotPresence : (Decode.Value -> msg) -> Sub msg
port gotPresenceState : (Decode.Value -> msg) -> Sub msg
port gotPresenceDiff : (Decode.Value -> msg) -> Sub msg
port gotVote : (Decode.Value -> msg) -> Sub msg

View file

@ -41,12 +41,25 @@ type Msg
| Reveal
| PlayerNameChanged String
| JoinRoom
| GotPresence Decode.Value
| GotPresence Presence
| GotPresenceDiff Decode.Value
| GotVote Decode.Value
| GotReveal
| GotReset
type Presence
= PresenceState (Dict String Player)
| PresenceDiff (Diff (Dict String Player))
| PresenceError Decode.Error
type alias Diff a =
{ joins : a
, leaves : a
}
type alias Room =
{ id : String
, name : String
@ -138,17 +151,30 @@ update key msg model =
, API.newProfile { playerName = model.playerName }
)
GotPresence value ->
case Decode.decodeValue playersDecoder value of
Ok players ->
let
newRoom =
{ room | players = players }
in
( { model | room = newRoom }, Cmd.none )
GotPresence (PresenceState players) ->
let
newRoom =
{ room | players = players }
in
( { model | room = newRoom }, Cmd.none )
Err _ ->
( model, Cmd.none )
GotPresence (PresenceDiff { joins, leaves }) ->
let
newPlayers =
room.players
|> Dict.filter (\id _ -> not (Dict.member id leaves))
|> Dict.union joins
newRoom =
{ room | players = newPlayers }
in
( { model | room = newRoom }, Cmd.none )
GotPresence _ ->
( model, Cmd.none )
GotPresenceDiff _ ->
( model, Cmd.none )
GotVote value ->
case Decode.decodeValue voteDecoder value of
@ -194,7 +220,6 @@ view dimensions model =
let
device =
classifyDevice dimensions
|> Debug.log "device"
playerName =
Dict.get model.player model.room.players
@ -439,13 +464,41 @@ joinForm room playerName =
subscriptions : Sub Msg
subscriptions =
Sub.batch
[ API.gotPresence GotPresence
[ API.gotPresenceState (decodePresenceState >> GotPresence)
, API.gotPresenceDiff (decodePresenceDiff >> GotPresence)
, API.gotReset (\_ -> GotReset)
, API.gotReveal (\_ -> GotReveal)
, API.gotVote GotVote
]
decodePresenceState : Decode.Value -> Presence
decodePresenceState value =
case Decode.decodeValue playersDecoder value of
Ok players ->
PresenceState players
Err error ->
PresenceError error
decodePresenceDiff : Decode.Value -> Presence
decodePresenceDiff value =
let
diffDecoder =
Decode.map PresenceDiff <|
Decode.map2 Diff
(Decode.field "joins" playersDecoder)
(Decode.field "leaves" playersDecoder)
in
case Decode.decodeValue diffDecoder value of
Ok diff ->
diff
Err error ->
PresenceError error
playersDecoder : Decode.Decoder (Dict String Player)
playersDecoder =
let