@@ -44,10 +44,10 @@ configured earlier in the book.
4444## Socket Connections
4545
4646A helpful way to think about Phoenix channels is to think of data being sent
47- back and forth over the socket. We saw something very similar in this book
48- while working with the Phoenix framework. Clients make a _ request_ and the
49- server returns a _ response_ , and Phoenix uses a ` conn ` to store all the data
50- that gets communicated back and forth.
47+ back and forth over the socket. We saw something similar in this book while
48+ working with the Phoenix framework. Clients make a _ request_ and the server
49+ returns a _ response_ , and Phoenix uses a ` conn ` to store all the data that gets
50+ communicated back and forth.
5151
5252Channels use the same idea, but instead of a ` conn ` we use a ` socket `
5353connection. We can assign data to the socket, and then it's available on both
@@ -469,86 +469,63 @@ SaveScore ->
469469
470470Now that we have everything in place to push our data over the socket, we'll
471471just need to join the ` "score:platformer" ` channel and broadcast the player's
472- score. Below our ` initialSocket ` function, let's create a new function called
473- ` initialChannel ` .
472+ score. We'll accomplish this in two steps. First, we'll create a button that
473+ allows players to join the channel. Then, we'll add another button that enables
474+ us to save scores.
474475
475- ``` elm
476- initialChannel : Phoenix .Channel .Channel msg
477- initialChannel =
478- Phoenix . Channel . init " score:platformer"
479- ```
480-
481- We're going to adjust the ` initialSocket ` function so that it returns a tuple.
482- The first item in that tuple is what we'll use for the ` phxSocket ` field in our
483- ` initialModel ` , and then second item in the tuple will be used as the initial
484- command in our ` init ` function.
485-
486- Here is the full code for our ` initialModel ` , ` initPhxSocket ` , and ` init `
487- functions:
476+ Below our ` initialSocket ` function, let's start by creating a new function
477+ called ` initialChannel ` .
488478
489479``` elm
490- initialSocket : ( Phoenix .Socket .Socket Msg , Cmd (Phoenix .Message .Msg Msg ) )
491- initialSocket =
492- let
493- devSocketServer =
494- " ws://localhost:4000/socket/websocket"
495- in
496- devSocketServer
497- |> Phoenix . Socket . init
498- -- |> Phoenix.Socket.on "save_score" "score:platformer" SaveScore
499- |> Phoenix . Socket . join initialChannel
480+ initialChannel : Phoenix .Channel .Channel Msg
481+ initialChannel =
482+ " score:platformer"
483+ |> Phoenix . Channel . init
484+ |> Phoenix . Channel . on " save_score" SaveScoreSuccess
500485```
501486
502- We can create two new functions to identify which parts of the tuple we need :
487+ Next, we'll update our ` Msg ` type with a new ` JoinChannel ` message :
503488
504489``` elm
505- initialSocketJoin : Phoenix .Socket .Socket Msg
506- initialSocketJoin =
507- Tuple . first initialSocket
508-
509-
510- initialSocketCommand : Cmd (Phoenix .Message .Msg Msg )
511- initialSocketCommand =
512- Tuple . second initialSocket
490+ type Msg
491+ = CountdownTimer Time . Posix
492+ | GameLoop Float
493+ | JoinChannel
494+ | KeyDown String
495+ | NoOp
496+ | SaveScoreSuccess Encode . Value
497+ | SaveScoreError Encode . Value
498+ | SaveScore
499+ | PhoenixMsg ( Phoenix . Message . Msg Msg )
500+ | SetNewItemPositionX Int
513501```
514502
515- Now, we can set the ` phxSocket ` field in our ` initialModel ` to
516- ` initialSocketJoin ` :
503+ With our ` initialChannel ` and ` JoinChannel ` code in place, we can set up our
504+ ` update ` function. For the ` JoinChannel ` case, we're taking the existing socket
505+ connection with ` model.phxSocket ` and using ` Phoenix.join ` with our
506+ ` initialChannel ` function to join the channel and update it in the model.
517507
518508``` elm
519- initialModel : Model
520- initialModel =
521- { characterDirection = Right
522- , characterPositionX = 50
523- , characterPositionY = 300
524- , gameState = StartScreen
525- , itemPositionX = 500
526- , itemPositionY = 300
527- , itemsCollected = 0
528- , phxSocket = initialSocketJoin
529- , playerScore = 0
530- , timeRemaining = 10
531- }
532- ```
509+ update : Msg -> Model -> ( Model , Cmd Msg )
510+ update msg model =
511+ case msg of
512+ -- ...
533513
534- And we can update our ` init ` function with the ` initialSocketCommand ` to get
535- things up and running:
514+ JoinChannel ->
515+ let
516+ ( updatedSocket, updatedCommand ) =
517+ Phoenix . join PhoenixMsg initialChannel model. phxSocket
518+ in
519+ ( { model | phxSocket = updatedSocket }, updatedCommand )
536520
537- ``` elm
538- init : () -> ( Model , Cmd Msg )
539- init _ =
540- ( initialModel, Cmd . map PhoenixMsg initialSocketCommand )
521+ -- ...
541522```
542523
543- This is a lot to process, but let's keep moving for now so we can get things
544- working, and we'll work towards a deeper understanding as we gain more
545- familiarity with the code we're working with.
546-
547- ## Triggering SaveScore
524+ ## Triggering JoinChannel and SaveScore
548525
549- Finally, we just need to trigger the ` SaveScoreRequest ` message whenever we
550- want to send our score over the socket. We'll set it up so that we can click a
551- button and then check the DevTools console to view the ` payload ` that's being
526+ Finally, we just need to trigger the ` JoinChannel ` and ` SaveScore ` messages
527+ to send our score over the socket. We'll set it up so that we can click a
528+ button and then check the server console to view the ` payload ` that's being
552529sent over the socket.
553530
554531At the top of our file, let's import the ` onClick ` functionality from
@@ -562,63 +539,84 @@ import Html.Events exposing (onClick)
562539
563540## Adding a Button to the View
564541
565- Now, we can create a new ` viewSaveScoreButton ` function and add it to our main
566- ` view ` to trigger the ` SaveScoreRequest ` message.
542+ Now, we can create new ` viewJoinChannelButton ` and ` viewSaveScoreButton `
543+ functions and add them to our main ` view ` to trigger the ` JoinChannel ` and
544+ ` SaveScore ` messages.
567545
568546``` elm
569547view : Model -> Html Msg
570548view model =
571549 div []
572550 [ viewGame model
551+ , viewJoinChannelButton
573552 , viewSaveScoreButton
574553 ]
575554
576555
556+ viewJoinChannelButton : Html Msg
557+ viewJoinChannelButton =
558+ div []
559+ [ button [ onClick JoinChannel , class " button" ]
560+ [ text " Join Channel" ]
561+ ]
562+
563+
577564viewSaveScoreButton : Html Msg
578565viewSaveScoreButton =
579566 div []
580- [ button [ onClick SaveScoreRequest , class " button" ]
567+ [ button [ onClick SaveScore , class " button" ]
581568 [ text " Save Score" ]
582569 ]
583570```
584571
585- Add ` Debug.log ` to ` SaveScoreRequest ` ?
586-
587572## Testing Out the Socket
588573
589574We've got everything configured, and we should be able to test out our working
590- socket payload. Start the Phoenix server with ` mix phx.server ` and load the
591- game in the browser. Collect a few coins to increment the player's score, and
592- then click the new "Save Score" button.
575+ socket payload. Here are the steps to try it:
576+
577+ - Start the server with ` mix phx.server ` and load the game in the browser.
578+ - Play the game and collect a few coins to increment the player's score.
579+ - Click the "Join Channel" button.
580+ - Click the "Save Score" button.
593581
594- The DevTools console will show an initial message when the page loads, and this
595- let's us know that our ` "score:platformer" ` topic was initialized with an ` "ok" `
596- status.
582+ Although we won't see any changes in the UI, we should be able to check the
583+ server console and see things working.
584+
585+ When the game loads on the page we see that we connect to the socket:
597586
598587``` shell
599- SaveScoreRequest: ({ event = " save_score" , topic = " score:platformer" , playerScore = 500, timeRemaining = 5 })
588+ [info] GET /games/platformer
589+ [info] CONNECT PlatformWeb.UserSocket
590+ [info] Replied PlatformWeb.UserSocket :ok
600591```
601592
602- We also see a message triggered in the DevTools console when we click the score
603- text. It shows that we triggered a ` "save_score" ` event, which means we can
604- broadcast the data over the socket. This is the behavior we want, because we'll
605- want all the player scores to be visible and update in real-time on the Phoenix
606- page where we list out all the players. In this example, you can see that we
607- collected three coins for a score of ` 300 ` , and that is reflected in the
608- payload with ` { score = 300 } ` .
593+ Then, when we click the "Join Channel" button we see the successful join
594+ message:
609595
610596``` shell
611- Phoenix message: { event = " save_score" , topic = " score:platformer" , payload = { player_score = 300 }, ref = Nothing }
597+ [info] JOIN " score:platformer" to PlatformWeb.ScoreChannel
598+ [info] Replied score:platformer :ok
612599```
613600
614- ! [Working Socket Payload](images/phoenix_channel_setup/working_socket_payload.png)
601+ Lastly, when we click the "Save Score" button we see the data being sent over
602+ the socket:
615603
616- # # Summary
604+ ``` shell
605+ [debug] INCOMING " save_score" on " score:platformer" to PlatformWeb.ScoreChannel
606+ Parameters: %{" player_score" => 300}
607+ ```
617608
618- We made it a _long_ way in this chapter, but we still haven' t accomplished our
619- goal of syncing data between the Elm front-end and Phoenix back-end.
609+ When we trigger the ` "save_score" ` event, we're broadcasting the data over the
610+ socket. The broadcast allows us to send the data in real-time to any other
611+ players connected to the socket. In the example above, you can see that we
612+ collected three coins for a score of ` 300 ` , and that is reflected in the
613+ payload with ` %{"player_score" => 300} ` .
614+
615+ ## Summary
620616
621- We created a new Phoenix channel, we installed elm-phoenix-socket, and we
622- configured our game to send a payload. But we still need to handle the data
623- that' s being sent from the front-end. Let' s take a look at these topics in the
624- next chapter.
617+ We made it a _ long_ way in this chapter. We managed to take care of most of the
618+ hard work, and we're successfully sending data from the Elm front-end to the
619+ Phoenix back-end. We created a new Phoenix channel, installed a package, and
620+ configured our game to send a payload. But we're not displaying the player
621+ scores in our application or persisting the data anywhere yet. We'll take a
622+ look at these topics in the next chapter.
0 commit comments