Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add presence support #32

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion assets/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,55 @@ channel.on('shout', function (payload) { // listen to the 'shout' event
}
});

/*
* When a user joins the channel, we ask Phoenix (our app) to push back the current state
* of online users.
*/
channel.on('presence_state', function(onlineUsers){
const currentlyOnlineUsers = Object.keys(onlineUsers)
updateOnlineList(currentlyOnlineUsers)
})

/*
* Every time a user joins (in case of this example, its when they send actual
* message and not on channel join), phoenix broadcasts a diff of joins and leaves,
* which can be used to modify the online users list in the view
*/
channel.on('presence_diff', function(userDiffPayload){
const currentlyOnlineUsers = Object.keys(userDiffPayload.joins)
const usersWhichLeft = Object.keys(userDiffPayload.leaves)
updateOnlineList(currentlyOnlineUsers)
removeOfflineUsers(usersWhichLeft)
})

// Helper function to append users to list
function updateOnlineList(users) {
for (var i = users.length - 1; i >= 0; i--) {
const userName = users[i]
console.log("User: '"+userName+"' is in the room")

if (document.getElementById(userName) == null) {
var li = document.createElement("li"); // create new user list item DOM element
li.id = userName
li.innerHTML = `<caption>${sanitise(userName)}</caption>`
usersList.appendChild(li); // append to userslist
}
}
}

// Helper function to remove users which have left the chat room
function removeOfflineUsers(users) {
for (var i = users.length - 1; i >= 0; i--) {
const userName = users[i]
console.log("User: '"+userName+"' left from the room")

const userWhichLeft = document.getElementById(userName)
if (userWhichLeft != null) {
usersList.removeChild(userWhichLeft); // remove the user from list
}
}
}

/**
* sanitise input to avoid XSS see: https://git.io/fjpGZ
* function borrowed from: https://stackoverflow.com/a/48226843/1148249
Expand All @@ -59,7 +108,7 @@ channel.join() // join the channel.
var ul = document.getElementById('msg-list'); // list of messages.
var name = document.getElementById('name'); // name of message sender
var msg = document.getElementById('msg'); // message input field

var usersList = document.getElementById('online-users'); // list of users.
// "listen" for the [Enter] keypress event to send a message:
msg.addEventListener('keypress', function (event) {
if (event.keyCode == 13 && msg.value.length > 0) { // don't sent empty msg.
Expand Down
3 changes: 2 additions & 1 deletion lib/chat/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ defmodule Chat.Application do
# Start the Ecto repository
supervisor(Chat.Repo, []),
# Start the endpoint when the application starts
supervisor(ChatWeb.Endpoint, [])
supervisor(ChatWeb.Endpoint, []),
# Start your own worker by calling: Chat.Worker.start_link(arg1, arg2, arg3)
# worker(Chat.Worker, [arg1, arg2, arg3]),
Chat.Presence
]

# See https://hexdocs.pm/elixir/Supervisor.html
Expand Down
5 changes: 5 additions & 0 deletions lib/chat/presence.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
defmodule Chat.Presence do
use Phoenix.Presence,
otp_app: :chat,
pubsub_server: Chat.PubSub
end
28 changes: 27 additions & 1 deletion lib/chat_web/channels/room_channel.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
defmodule ChatWeb.RoomChannel do
use ChatWeb, :channel
alias Phoenix.Socket
alias Chat.Presence

def join("room:lobby", payload, socket) do
if authorized?(payload) do
Expand All @@ -23,12 +25,19 @@ defmodule ChatWeb.RoomChannel do
Chat.Message.changeset(%Chat.Message{}, payload)
|> Chat.Repo.insert()

broadcast(socket, "shout", Map.put_new(payload, :id, msg.id))
socket
|> Socket.assign(:user_name, msg.name)
|> track_presence()
|> broadcast("shout", Map.put_new(payload, :id, msg.id))

{:noreply, socket}
end

# example see: https://git.io/vNsYD
def handle_info(:after_join, socket) do
# Send currently online users in lobby
push(socket, "presence_state", Presence.list("room:lobby"))

# get messages 10 - 1000
Chat.Message.get_messages()
|> Enum.each(fn msg ->
Expand All @@ -47,4 +56,21 @@ defmodule ChatWeb.RoomChannel do
defp authorized?(_payload) do
true
end

# Add a track in Presence when user joins the channel
# Ideally this should be on joining "room:lobby" but the socket has no info as of now
# to associate with a user.

defp track_presence(socket) do
case do_track(socket) do
{:ok, _ref} -> socket
{:error, {:already_tracked, _pid, _channel, _user}} -> socket
end
end

defp do_track(%{assigns: %{user_name: user_name}} = socket) when not is_nil(user_name) do
Presence.track(socket, user_name, %{
online_at: inspect(System.system_time(:second))
})
end
end
16 changes: 13 additions & 3 deletions lib/chat_web/templates/page/index.html.eex
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
<div class="row">
<div class="col-xs-12 col-sm-3">
<ul id="online-users" style="list-style: none; min-height:200px;
border: 1px solid #e5e5e5; padding: 10px">
<li class="text-left"><b><u>Online users</u></b></li>
</ul>
</div>
<!-- The list of messages will appear here: -->
<ul id="msg-list" style="list-style: none; min-height:200px;
border: 1px solid #e5e5e5; margin: 10px; padding: 10px;">
<div class="col-xs-12 col-sm-9">
<ul id="msg-list" style="list-style: none; min-height:200px;
border: 1px solid #e5e5e5; padding: 10px">
<%= for m <- @messages do %>
<li id="<%= m.id %>"><b> <%= m.name %> </b> <%= m.message %> </li>
<li id="<%= m.id %>"><b> <%= m.name %> </b> <%= m.message %> </li>
<% end %>
</ul>

Expand All @@ -14,3 +22,5 @@
<input type="text" id="msg" class="form-control" placeholder="Your Message">
</div>
</div>
</div>
</div>