diff --git a/web/channels/system_channel.ex b/web/channels/system_channel.ex index b9b598b..2a74fd0 100644 --- a/web/channels/system_channel.ex +++ b/web/channels/system_channel.ex @@ -1,38 +1,33 @@ defmodule KolabChat.SystemChannel do use KolabChat.Web, :channel alias KolabChat.Presence - def join("system", _, socket) do - Process.flag(:trap_exit, true) - :timer.send_interval(10000, :ping) + 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 - Presence.track(socket, socket.assigns.user.username, %{ - status: "online" - }) - - push socket, "info", %{user: socket.assigns.user.username} push socket, "presence_state", Presence.list(socket) + push socket, "info", %{user: socket.assigns.user.username} - {:noreply, socket} - end - - def handle_info(:ping, socket) do - push socket, "new:msg", %{user: "SYSTEM", body: "ping"} + Presence.track(socket, socket.assigns.user.username, %{ + status: "online", + context: socket.assigns.context + }) {:noreply, socket} end def handle_in("set-status", %{"status" => status}, socket) do {:ok, _} = Presence.update(socket, socket.assigns.user.username, %{ - status: status + status: status, + context: socket.assigns.context }) {:noreply, socket} end end diff --git a/web/static/js/api.js b/web/static/js/api.js index 5373a44..7baa4ad 100644 --- a/web/static/js/api.js +++ b/web/static/js/api.js @@ -1,198 +1,218 @@ import {Socket, LongPoll, Presence} from "phoenix" import UserListWidget from "./widgets/userlist" import UserStatusWidget from "./widgets/userstatus" import ChatInputWidget from "./widgets/chatinput" import ChatRoomWidget from "./widgets/chatroom" class KolabChat { /** * 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 * - chatInputElement: Id of HTML element which is a text chat input * - chatRoomElement: Id of HTML element where to put text conversation */ constructor(config) { this.config = config || {} + + if (!this.config.context) + this.config.context = location.hostname; } /** * Initialize WebSocket communication */ init(config) { if (config) this.config = KolabChat.extend(this.config, config) this.initWidgets() // 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) }), }) this.socket.onOpen(e => { // when connected start using 'system' channel // for users' presence this.initPresence() if (this.config.roomId) { this.initRoom(this.config.roomId) } }) this.socket.connect() } /** * Initializes configured UI widgets */ initWidgets() { let config if (this.config.userListElement && $('#' + this.config.userListElement).length) { config = { username: this.username, openChat: (e, user) => { this.openChat(e, user) } } this.userListWidget = new UserListWidget(this.config.userListElement, config) } if (this.config.userStatusElement && $('#' + this.config.userStatusElement).length) { config = { username: this.username, statusChange: status => { this.setStatus(status) } } this.userStatusWidget = new UserStatusWidget(this.config.userStatusElement, config) } if (this.config.chatRoomElement && $('#' + this.config.chatRoomElement).length) { this.chatRoomWidget = new ChatRoomWidget(this.config.chatRoomElement) } if (this.config.chatInputElement && $('#' + this.config.chatInputElement).length) { config = { submit: (e, msg) => { this.sendTxtMessage(e, msg) } } this.chatInputWidget = new ChatInputWidget(this.config.chatInputElement, config) } } /** * Initialize user presence * Create users list and status widgets */ 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 }) this.system.on("presence_state", state => { this.presences = Presence.syncState({}, state) this.renderPresences(this.presences) }) this.system.on("presence_diff", diff => { // ignore initial presence_diff result, handle presence_state first if (this.presences !== undefined) { this.presences = Presence.syncDiff(this.presences, diff) this.renderPresences(this.presences) } }) this.system.join() } /** * Initialize chat channel */ initRoom(roomId) { this.chat = this.socket.channel("room:" + roomId) this.chat.on("new:message", message => { this.chatRoomWidget.append(message.user, message.body) }) this.chat.join() } /** * Send text message to the chat room */ sendTxtMessage(event, message) { this.chat.push("new:message", { user: this.username, // TODO: this is not really needed body: message }) } /** * Handler for presence responses */ renderPresences(presences) { 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) } if (this.userListWidget) { presences = Presence.list(presences, this.listBy) this.userListWidget.render(presences) } } - 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 } } /** * User status change */ setStatus(status) { this.system.push('set-status', {status: status}); } /** * Open chat window (and create a new chat room) */ openChat(event, user) { // TODO: Use 'system' channel to create a chat room first let roomId = "lobby" let windowName = 'KolabChat' + new Date().getTime() let url = "/chat/" + encodeURIComponent(roomId) + "/?token=" + encodeURIComponent(this.config.token) var extwin = window.open(url, windowName); } static extend(obj, src) { Object.keys(src).forEach(function(key) { obj[key] = src[key] }) return obj } } export default KolabChat