Permalink
Browse files

Pages now draw to their canvas locally and broadcast to the other pages.

  • Loading branch information...
michaeljbishop committed Oct 23, 2016
1 parent 9ab27c8 commit 1c0441d4549b82fee77c8328b6c69faddfdc092a
Showing with 43 additions and 15 deletions.
  1. +15 −2 web/channels/room_channel.ex
  2. +9 −2 web/channels/user_socket.ex
  3. +16 −10 web/static/js/app.js
  4. +1 −1 web/static/js/socket.js
  5. +2 −0 web/templates/page/index.html.eex
@@ -1,15 +1,28 @@
defmodule Draw.RoomChannel do
use Phoenix.Channel
+ intercept ["drawLines"]
+
def join("room:drawing", _message, socket) do
{:ok, socket}
end
def join("room:" <> _private_room_id, _params, _socket) do
{:error, %{reason: "unauthorized"}}
end
- def handle_in("drawLines", %{"lines" => lines}, socket) do
- broadcast! socket, "drawLines", %{lines: lines}
+ def handle_in("drawLines", %{"canvas_id" => canvas_id, "lines" => lines}, socket) do
+ broadcast! socket, "drawLines", %{canvas_id: canvas_id, lines: lines}
{:noreply, socket}
end
+
+ def handle_out("drawLines", %{canvas_id: canvas_id, lines: lines}, socket) do
+ # Pages draw locally to their own canvas before sending out draw events so
+ # we don't rebroadcast them the event they sent the server.
+ if canvas_id === socket.assigns.canvas_id do
+ {:noreply, socket}
+ else
+ push socket, "drawLines", %{lines: lines}
+ {:noreply, socket}
+ end
+ end
end
@@ -9,6 +9,13 @@ defmodule Draw.UserSocket do
timeout: 45_000
# transport :longpoll, Phoenix.Transports.LongPoll
+ # Used to identify a canvas when a page is loaded.
+ # Since a page draws locally to its own canvas AND sends
+ # events to the server, we use this to ensure a page
+ # doesn't receive drawLine events for itself (drawing
+ # twice)
+ def canvas_id(), do: System.os_time
+
# Socket params are passed from the client and can
# be used to verify and authenticate a user. After
# verification, you can put default assigns into
@@ -20,8 +27,8 @@ defmodule Draw.UserSocket do
#
# See `Phoenix.Token` documentation for examples in
# performing token verification on connect.
- def connect(_params, socket) do
- {:ok, socket}
+ def connect(%{"canvas_id" => canvas_id}, socket) do
+ {:ok, assign(socket, :canvas_id, String.to_integer(canvas_id))}
end
# Socket id's are topics that allow you to identify all sockets for a given user:
View
@@ -28,21 +28,27 @@ channel.join()
var canvas = document.getElementById("draw-canvas");
var ctx = canvas.getContext("2d");
-function drawLines(lines) {
- // Send a message to draw a line (we'll draw when we receive it)
- channel.push("drawLines", {lines: lines});
-}
-
-// Draw whatever we receive
-channel.on("drawLines", payload => {
- var _lines = payload.lines;
- for (var i = 0; i < _lines.length; i++) {
- var line = _lines[i];
+function _drawLines(lines) {
+ for (var i = 0; i < lines.length; i++) {
+ var line = lines[i];
ctx.moveTo(line.from.x, line.from.y);
ctx.lineTo(line.to.x, line.to.y);
ctx.stroke();
}
+}
+
+function drawLines(lines) {
+ _drawLines(lines);
+
+ // If we send our canvasID, the server won't waste bandwidth sending
+ // us our own drawLines messages.
+ channel.push("drawLines", {lines: lines, canvas_id: window.canvasID});
+}
+
+// Draw whatever we receive
+channel.on("drawLines", payload => {
+ _drawLines(payload.lines)
})
// ------------------------
View
@@ -5,7 +5,7 @@
// and connect at the socket path in "lib/my_app/endpoint.ex":
import {Socket} from "phoenix"
-let socket = new Socket("/socket", {params: {token: window.userToken}})
+let socket = new Socket("/socket", {params: {canvas_id: window.canvasID, token: window.userToken}})
// When you connect, you'll often need to authenticate the client.
// For example, imagine you have an authentication plug, `MyAuth`,
@@ -1,2 +1,4 @@
+<% # We seed the canvas id to be sent back in drawing messages %>
+<script>window.canvasID = <%= Draw.UserSocket.canvas_id %></script>
<canvas id="draw-canvas" class="bordered" width="600" height="600">
</canvas>

0 comments on commit 1c0441d

Please sign in to comment.