I just started doing that on my own (before the rewrite last year). I modified web/templates/layout/app.html.eex to include:
</div> <!-- /container -->
<script src="<%= static_path(@conn, "/js/app.js") %>"></script>
<!-- BEGIN Demo code for "B2.0 7. Create Persistent Connections With Phoenix Channels" -->
<script type="text/javascript">
var Demo = (function () {
var demo = {
connect: connect,
new_channel: new_channel,
join: join,
leave: leave,
say_hello: say_hello,
new_game: new_game,
add_player: add_player,
set_island_coordinates: set_island_coordinates,
set_islands: set_islands,
guess_coordinate: guess_coordinate,
p1_155: p1_155,
p1_157: p1_157,
p1_159: p1_159,
p1_161: p1_161,
p2_162: p2_162,
p1_163: p1_163,
p1_165: p1_165,
p1_167: p1_167,
p2_167: p1_167,
p2_167b: p2_167b,
p1_169: p1_169,
p2_170: p2_170,
p1_171: p1_171,
p2_171: p1_171,
p1_172: p1_172,
p2_172: p2_172,
p1_173: p1_173,
p2_173: p1_173,
p1_174: p1_174,
p2_174: p2_174,
p1_178: () => p1_178("moon"),
p2_178: () => p1_178("diva"),
p1_179: p1_179,
p2_179: p1_179,
p1_181: p1_181,
p2_181: p1_181,
p3_181: p1_181,
p1_181b: () => p1_181b("moon"),
p2_181b: () => p1_181b("diva"),
p3_181b: () => p1_181b("nope"),
}
return demo;
//---
function connect() {
// B2.0 p.155
console.log('> var phoenix = require("phoenix")');
demo.phoenix = require("phoenix");
console.log('> var socket = new phoenix.Socket("/socket", {})');
demo.socket = new demo.phoenix.Socket("/socket", {});
console.log('> socket.connect()');
demo.socket.connect();
}
// B2.0 p.155
function new_channel(player, screen_name) {
return demo.socket.channel("game:" + player, {screen_name: screen_name});
}
// B2.0 p.156
function join(channel) {
channel.join()
.receive("ok", response => {
console.log("Joined successfully!", response);
})
.receive("error", response => {
console.log("Unable to join", response);
});
}
// B2.0 p.157
function leave(channel) {
channel.leave()
.receive("ok", response => {
console.log("Left successfully", response);
})
.receive("error", response => {
console.log("Unable to leave", response);
})
}
// B2.0 p.159
function say_hello(channel, greeting) {
channel.push("hello", {"message": greeting})
.receive("ok", response => {
console.log("Hello", response.message);
})
.receive("error", response => {
console.log("Unable to say hello to the channel.", response.message);
})
}
// B2.0 p.165
function new_game(channel) {
channel.push("new_game")
.receive("ok", response => {
console.log("New Game!", response);
})
.receive("error", response => {
console.log("Unable to start a new game.", response);
})
}
// B2.0 p.167
function add_player(channel, player) {
channel.push("add_player", player)
.receive("error", response => {
console.log("Unable to add new player: " + player, response)
})
}
// B2.0 p.169
function set_island_coordinates(channel, player, island, coordinates) {
params = {"player": player, "island": island, "coordinates": coordinates};
channel.push("set_island_coordinates", params)
.receive("ok", response => {
console.log("New coordinates set!", response);
})
.receive("error", response => {
console.log("Unable to set new coordinates.", response)
})
}
// B2.0 p.171
function set_islands(channel, player) {
channel.push("set_islands", player)
.receive("error", response => {
console.log("Unable to set islands for: " + player, response);
})
}
// B2.0 p.173
function guess_coordinate(channel, player, coordinate) {
params = {"player": player, "coordinate": coordinate}
channel.push("guess_coordinate", params)
.receive("error", response => {
console.log("Unable to guess a coordinate: " + player, response);
})
}
function p1_155() {
demo.connect();
console.log('> var game_channel = new_channel("moon", "moon")');
demo.game_channel = demo.new_channel("moon","moon");
console.log(demo.game_channel)
}
function p1_157() {
console.log('> join(game_channel)');
demo.join(demo.game_channel);
}
// use for B2.0 159, 160, 161
function p1_159() {
console.log('> say_hello(game_channel, "World!")');
demo.say_hello(demo.game_channel, "World!");
}
function p1_161() {
console.log('game_channel.on("said_hello", response => {...})');
demo.game_channel.on("said_hello", response => {
console.log("Returned Greeting:", response.message)
});
console.log('> say_hello(game_channel, "World!")');
demo.say_hello(demo.game_channel, "World!");
}
function p2_162() {
demo.connect();
console.log('> var game_channel = new_channel("moon", "diva")');
demo.game_channel = demo.new_channel("moon","diva");
console.log('> join(game_channel)');
demo.join(demo.game_channel);
console.log('> game_channel.on("said_hello", response => {...})');
demo.game_channel.on("said_hello", response => {
console.log("Returned Greeting:", response.message)
});
}
function p1_163() {
console.log('> say_hello(game_channel, "World!")');
demo.say_hello(demo.game_channel, "World!");
}
function p1_165() {
console.log('> new_game(game_channel)');
demo.new_game(demo.game_channel);
}
// use for player2 (p.167) as well
function p1_167() {
console.log('> game_channel.on("player_added", response => {...})');
demo.game_channel.on("player_added", response => {
console.log("Player Added", response)
});
}
function p2_167b() {
console.log('> add_player(game_channel, "diva")');
demo.add_player(demo.game_channel, "diva");
}
function p1_169() {
console.log('> set_island_coordinates(game_channel, "player1", "atoll", ["a1"])');
demo.set_island_coordinates(demo.game_channel, "player1", "atoll", ["a1"]);
console.log('> set_island_coordinates(game_channel, "player1", "dot", ["a1"])');
demo.set_island_coordinates(demo.game_channel, "player1", "dot", ["a1"]);
console.log('> set_island_coordinates(game_channel, "player1", "l_shape", ["a1"])');
demo.set_island_coordinates(demo.game_channel, "player1", "l_shape", ["a1"]);
console.log('> set_island_coordinates(game_channel, "player1", "s_shape", ["a1"])');
demo.set_island_coordinates(demo.game_channel, "player1", "s_shape", ["a1"]);
console.log('> set_island_coordinates(game_channel, "player1", "square", ["a1"])');
demo.set_island_coordinates(demo.game_channel, "player1", "square", ["a1"]);
}
function p2_170() {
console.log('> set_island_coordinates(game_channel, "player2", "atoll", ["c10"])');
demo.set_island_coordinates(demo.game_channel, "player2", "atoll", ["c10"]);
}
// use for player2 (p.171) as well
function p1_171() {
console.log('> game_channel.on("player_set_islands", response => {...})');
demo.game_channel.on("player_set_islands", response => {
console.log("Player Set Islands", response);
});
}
function p1_172() {
console.log('> set_islands(game_channel, "player1")');
demo.set_islands(demo.game_channel, "player1");
}
function p2_172() {
console.log('> set_islands(game_channel, "player2")');
demo.set_islands(demo.game_channel, "player2");
}
// use for player2 (p.173) as well
function p1_173() {
console.log('> game_channel.on("player_guessed_coordinate", response => {...})');
demo.game_channel.on("player_guessed_coordinate", response => {
console.log("Player Guessed Coordinate: ", response.result);
});
}
function p1_174() {
console.log('> guess_coordinate(game_channel, "player1", "b10")');
demo.guess_coordinate(demo.game_channel, "player1","b10");
}
function p2_174() {
console.log('> guess_coordinate(game_channel, "player2", "a1")');
demo.guess_coordinate(demo.game_channel, "player2","a1");
}
function p1_178(name){
demo.connect();
console.log(`> var game_channel = new_channel("moon", "${name}")`);
demo.game_channel = demo.new_channel("moon", name);
console.log('> game_channel.on("subscribers", response => {...})');
demo.game_channel.on("subscribers", response => {
console.log("These players have joined: ", response);
});
}
function p1_179() {
console.log('> join(game_channel)');
demo.join(demo.game_channel);
console.log('> game_channel.push("show_subscribers")');
demo.game_channel.push("show_subscribers");
}
function p1_181(){
demo.connect();
}
function p1_181b(name){
console.log(`> var game_channel = new_channel("moon", "${name}")`);
demo.game_channel = demo.new_channel("moon", name);
console.log('> join(game_channel)');
demo.join(demo.game_channel);
}
}());
</script>
<!-- END Demo code for "B2.0 7. Create Persistent Connections With Phoenix Channels" -->
</body>
Then in the browser console I just fiddled with:
player1 console // Annotation
-- player2 console
Demo.p1_155(); // Section: Establish a Client Connection
Demo.p1_157();
Demo.p1_159(); // Section: Converse Over a Channel: "Hello World!"
Demo.p1_159(); // "We forced this error."
Demo.p1_159(); // "And we get nothing."
Demo.p1_161();
-- Demo.p2_162();
Demo.p1_163();
Demo.p1_165(); // Section: Connect the Channel to the Game - Start a New Game
Demo.p_165();
Demo.p_165(); // "Unable to start a new game."
Demo.p1_167(); // Section: Add a second player
-- Demo.p2_167();
-- Demo.p2_167b();
Demo.p1_169(); // Section: Setting Island Coordinates
-- Demo.p2_170();
Demo.p1_171();
-- Demo.p2_171();
Demo.p1_172();
-- Demo.p2_172();
Demo.p1_173(); // Section: Guessing Coordinates
-- Demo.p2_173();
Demo.p1_174();
-- Demo.p2_174();
player1 console
-- player2 console
Demo.p1_178(); // Section: Phoenix Presence
-- Demo.p2_178();
Demo.p1_179();
-- Demo.p1_179();
player1 console
-- player2 console
-- -- player3 console
Demo.p1_181(); // Section: Authorization
-- Demo.p2_181();
-- -- Demo.p3_181();
Demo.p1_181b();
-- Demo.p2_181b();
-- -- Demo.p3_181b();
Posted about it in the pragprog forums - which unfortunately are inaccessible right now.


















