loading...

Pixel Wars: Using Pusher in Elm

avalander profile image Avalander ・2 min read

View the project on GitHub

Since I'm using Elm in my project, I needed to figure out how to use Pusher in Elm, because there is no Pusher client for it. Fortunately, Elm offers a very clean and nice way to interact with Javascript. I was positively surprised by how easy it actually was.

Using Pusher in Elm

Let's start with the code. Here's how I did it.

First, I declared two ports in an Elm module.

port module Pusher exposing (..)

port messages : (Pony -> msg) -> Sub msg

port connect : String -> Cmd msg
  • messages will be invoked from Javascript every time the Pusher subscription receives a message. Nevermind the type Pony, that's going to change when I implement updating the game state in the backend.
  • connect will be invoked from Elm to create the Pusher connection. Javascript will listen to that message and open the connection.

Let's have a look at the Javascript code required to make this work.

import Elm from 'app/Main.elm'
import Pusher from 'pusher-js'

const mount_node = document.querySelector('#root')
const app = Elm.Main.embed(mount_node)
let pusher

app.ports.connect.subscribe(() => {
    pusher = new Pusher(PUSHER_KEY, {
        encrypted: true,
    })
    pusher.subscribe('ponies')
        .bind('pony-data', app.ports.messages.send)
})

All functions prefixed with port in Elm are exposed in Javascript in the object app.ports. Therefore, I can call app.ports.connect.subscribe to run a callback in Javascript when the Pusher.connect command is executed in Elm. In a similar fashion, I can call app.ports.messages.send to invoke the function messages in Elm.

To glue everything in Elm, I need to subscribe to the function messages, and return the command connect in the update function when I want to connect to pusher. Here's how I did it in my Main.elm.

import Pusher

type Msg
    = OnPusherMessage Pony
    | OnFetchGame (WebData GameResponse)

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    case msg of
        OnPusherMessage pony ->
            ({ model | name = pony.name }, Cmd.none)
        OnFetchGame response ->
            (
                updateModel model response,
                Pusher.connect "connect"
            )

subscriptions : Model -> Sub Msg
subscriptions model =
    Pusher.messages OnPusherMessage

There we go. Adding Pusher.messages in the subscriptions will trigger a OnPusherMessage with the received message every time app.ports.message.send is invoked in Javascript. Similarly, returning Pusher.connect in update will send an event to app.ports.connect when the client has received the game state, so that updates can start flowing.

Next Steps

The next step is to make the client send clicks to the server and have the server update the game state and send it back to all connected clients. Hopefully I'll have something functional enough to be deployed in a couple of days.

Posted on May 5 '18 by:

avalander profile

Avalander

@avalander

"Biography is irrelevant" - Seven of nine (probably) if she was asked that question.

Discussion

markdown guide
 

Thank you so much for sharing this!