diff --git a/web/channels/system_channel.ex b/web/channels/system_channel.ex --- a/web/channels/system_channel.ex +++ b/web/channels/system_channel.ex @@ -3,36 +3,59 @@ alias KolabChat.Presence - def join("system", _, socket) do - Process.flag(:trap_exit, true) - :timer.send_interval(10000, :ping) + @status [ + # user is available for chat + :online, + :away, + # user is connected and visible, but not available + :busy, + :unavailable, + # user is shown as offline + :invisible, + :offline + ] + + def join("system", %{"context" => context}, socket) do + socket = assign(socket, :context, context) send self(), :after_join {:ok, socket} end def handle_info(:after_join, socket) do + push socket, "presence_state", Presence.list(socket) + push socket, "info", %{user: socket.assigns.user.username} + Presence.track(socket, socket.assigns.user.username, %{ - status: "online" + status: :online, + context: socket.assigns.context }) - push socket, "info", %{user: socket.assigns.user.username} - push socket, "presence_state", Presence.list(socket) - {:noreply, socket} end - def handle_info(:ping, socket) do - push socket, "new:msg", %{user: "SYSTEM", body: "ping"} - + def handle_in("set-status", %{"status" => status}, socket) do + update_presence_status(check_status(status), socket) {:noreply, socket} end - def handle_in("set-status", %{"status" => status}, socket) do + defp update_presence_status(:invalid, _socket), do: :ok + defp update_presence_status(status, socket) do {:ok, _} = Presence.update(socket, socket.assigns.user.username, %{ - status: status + status: status, + context: socket.assigns.context }) + end - {:noreply, socket} + # Makes sure the provided status name is supported + # Returns status name as an atom + defp check_status(status) do + status = String.to_atom(status) + + if Enum.member?(@status, status) do + status + else + :invalid + end end end diff --git a/web/static/css/widgets.css b/web/static/css/widgets.css --- a/web/static/css/widgets.css +++ b/web/static/css/widgets.css @@ -1,15 +1,18 @@ /* Style for chat application widgets */ -.status-online .glyphicon-user { +.status-online .glyphicon-user, +.status-away .glyphicon-user { color: green; } -.status-away .glyphicon-user { - color: grey; +.status-busy .glyphicon-user, +.status-unavailable .glyphicon-user { + color: red; } -.status-busy .glyphicon-user { - color: red; +.status-invisible .glyphicon-user, +.status-offline .glyphicon-user { + color: gray; } .userlist .btn-group { diff --git a/web/static/js/api.js b/web/static/js/api.js --- a/web/static/js/api.js +++ b/web/static/js/api.js @@ -10,6 +10,7 @@ /** * Configuration parameters: * - token: User session token + * - context: KolabChat instance identifier * - roomId: Chat room Id to join in * - userListElement: Id of HTML element where to put userslist widget * - userStatusElement: Id of HTML element where to put users status widget @@ -19,6 +20,9 @@ constructor(config) { this.config = config || {} + + if (!this.config.context) + this.config.context = location.hostname; } /** @@ -34,7 +38,7 @@ // TODO: for integration with external systems we'll use configurable full wss:// url this.socket = new Socket("/socket", { - params: {token: this.config.token}, + params: {token: this.config.token, context: this.config.context}, logger: ((kind, msg, data) => { console.log(`${kind}: ${msg}`, data) }), }) @@ -91,7 +95,7 @@ */ initPresence() { - this.system = this.socket.channel("system") + this.system = this.socket.channel("system", {context: this.config.context}) this.system.on("info", info => { this.username = info.user @@ -146,7 +150,7 @@ let userPresence if (this.userStatusWidget && (userPresence = presences[this.username])) { - userPresence = this.listBy(this.username, userPresence) + userPresence = this.listBy(this.username, userPresence, this.config.context) this.userStatusWidget.render(userPresence) } @@ -156,11 +160,27 @@ } } - listBy(user, {metas: metas}) + listBy(user, {metas: metas}, context) { + let statusWeights = { + offline: 0, + busy: 10, + away: 20, + online: 30 + } + + // Find "best" availability status for the user + // If set, narrow the result to the current session context + let mostAvailableStatus = metas.reduce((best, meta) => { + if (context && context != meta.context) + return best + return statusWeights[best] > statusWeights[meta.status] ? best : meta.status + }, + "offline") + return { user: user, - status: metas[0].status + status: mostAvailableStatus } } diff --git a/web/static/js/widgets/userstatus.js b/web/static/js/widgets/userstatus.js --- a/web/static/js/widgets/userstatus.js +++ b/web/static/js/widgets/userstatus.js @@ -9,6 +9,19 @@ { this.config = config || {} this.id = id + + // FIXME: should we get that list from the backend? + this.status_list = [ + // user is available for chat + "online", + "away", + // user is connected and visible, but not available + "busy", + "unavailable", + // user is shown as offline + "invisible", + "offline" + ]; } /** @@ -18,6 +31,11 @@ { let userStatusElement = document.getElementById(this.id) let icon = '' + let options = list => { + return $.map(list, status => + `
  • ${icon} ${status}
  • ` + ).join("\n") + } userStatusElement.innerHTML = ` `