diff --git a/lib/CollaborationModule.js b/lib/CollaborationModule.js index 4844d42..71fc349 100644 --- a/lib/CollaborationModule.js +++ b/lib/CollaborationModule.js @@ -1,7 +1,7 @@ import { AbstractModule } from "adapt-authoring-core"; +import { json } from "express"; import path from "path"; import { WebSocketServer } from "ws"; -let wss; /** * Module to implement Totara connect API @@ -9,35 +9,19 @@ let wss; */ class CollaborationModule extends AbstractModule { /** @override */ + static get USER_STATUS() { + return { + ACTIVE: "ACTIVE", + IDLE: "IDLE" + }; + } async init() { - // do custom initialisation here - // const server = await this.app.waitForModule("server"); - /*const PORT = 5000; - const app = express(); - const server = http.createServer(app); - const wsServer = new WebSocket.Server({ - server: server - }); - - wsServer.on("connection", function connection(ws) { - ws.send("Welcome New Client!"); - - ws.on("message", function incoming(message) { - wsServer.clients.forEach(function each(client) { - if (client !== ws && client.readyState === WebSocket.OPEN) { - client.send(message); - } - }); - }); - }); - - app.get("/", (req, res) => res.send("Hello World!")); - - server.listen(3000, () => console.log(`Lisening on port :3000`)); - */ - this.sockets = []; + this.allActiveUsers = []; + this.activeUsers = {}; + this.idleTime = 1; + this.wss; await this.initRouter(); @@ -51,13 +35,11 @@ class CollaborationModule extends AbstractModule { auth.secureRoute(`${router.path}/data`, "GET", ["read:config"]); server.listeningHook.tap(() => { - wss = new WebSocketServer({ + this.wss = new WebSocketServer({ server: server.httpServer, path: "/socket" }); - wss.on("connection", this.onConnection.bind(this)); - - // setInterval(() => this.send("just testing " + Date.now()), 3000); + this.wss.on("connection", this.onConnection.bind(this)); }); const ui = await this.app.waitForModule("ui"); @@ -87,21 +69,71 @@ class CollaborationModule extends AbstractModule { } async onConnection(ws) { - console.log("new ws connection"); + ws.on("close", this.onClose.bind(this)); ws.on("message", this.onMessage.bind(this)); - // this.sockets.push(ws); + this.sockets.push(ws); } async onMessage(data) { - const dataTest = data.buffer.toString(); - console.log("ws message", dataTest); - wss.clients.forEach(function each(client) { - client.send(dataTest); - }); + const message = data.toString(); + const arr = message.split("::"); + + switch (arr[1]) { + case "newUser": + this.newUser(arr[0], arr[2]); + break; + case "idle": + this.checkIdle(arr[0], arr[2]); + break; + case "location": + this.updateLocation(arr[0], arr[2]); + break; + default: + console.log("no message found on switch"); + } + } + + async updateActiveUsers(currentUser) { + console.log(currentUser.userLocation) + const location = currentUser.userLocation + const notifyUsers = this.allActiveUsers.filter(user => user.userLocation === location); + const data = JSON.stringify(notifyUsers) + notifyUsers.forEach(user => user.socket.send(data)); + } + + async checkIdle(userID, status) { + // CollaborationModule.USER_STATUS.IDLE + const index = this.allActiveUsers.findIndex((x) => x.id === userID); + this.allActiveUsers[index].status = status + this.updateActiveUsers(this.allActiveUsers[index]); + } + + async updateLocation(userID, location) { + if (this.allActiveUsers === 0) return; + + const index = this.allActiveUsers.findIndex((x) => x.id === userID); + if (index === undefined || index === -1) return + + this.allActiveUsers[index].userLocation = location + this.updateActiveUsers(this.allActiveUsers[index]); + } + + async newUser(userID, user) { + let newUser = JSON.parse(user); + const index = this.allActiveUsers.findIndex((x) => x.id === userID); + + if (index !== -1) { + this.allActiveUsers.splice(index, 1).pop(); + } + + newUser.socket = this.sockets.slice(-1).pop(); + this.allActiveUsers.push(newUser); + this.updateActiveUsers(newUser); } - async send(data) { - this.sockets.forEach((s) => s.send(data)); + async onClose(data) { + const index = this.sockets.findIndex((socket) => socket === this); + this.sockets.splice(index, 1).pop(); } async returnUser(req, res, next) { diff --git a/plugins/adaptcollab/index.js b/plugins/adaptcollab/index.js index d0275a8..d320319 100644 --- a/plugins/adaptcollab/index.js +++ b/plugins/adaptcollab/index.js @@ -2,127 +2,81 @@ define(function (require) { var Origin = require("core/origin"); var AdaptCollabView = require("./views/adaptCollabView"); - var users = []; - let allActiveUsers = []; - const idleTime = 1; const [protocol, serverRoot] = window.location.origin.split("//"); - const socket = new WebSocket(`ws://${serverRoot}/socket`); + this.socket; + this.user = {}; Origin.on("login:changed", loadData); - Origin.on("user:logout", function () { - //socket.close(); - }); - Origin.on("origin:dataReady", function init() { loadData(); - - Origin.on("editorView:postRender", function () { - render(); - }); - - Origin.on("location:change", function () { - locationChange(); - }); }); - function render() { + function render(users) { $(".toast-container").empty(); - var acv = new AdaptCollabView(allActiveUsers, users[0]); + var acv = new AdaptCollabView(users); $(".toast-container").append(acv.$el); } - function loadData() { - $.get("/api/adaptcollab/getUser/") - .done(function (allUSers) { - users = allUSers; - locationChange(); - checkIdle(); - }) - .fail(function (jqXHR, textStatus, errorThrown) { - console.log(Origin); - }); - - socket.addEventListener("open", function (event) { - console.log("Connected to WS Server"); - }); - - socket.addEventListener("close", function (event) { - // const message = JSON.parse(event.data); - }); + function setupEventListeners() { + let timeout; + $("body,html").bind( + "touchstart touchmove scroll mousedown DOMMouseScroll mousewheel keyup", + () => { + if (timeout) { + clearTimeout(timeout); + if (this.user.status !== 'ACTIVE') { + this.user.status = 'ACTIVE' + this.socket.send(`${this.user.id}::idle::ACTIVE`) + } + } + timeout = setTimeout(() => { + this.user.status = 'INACTIVE' + this.socket.send(`${this.user.id}::idle::INACTIVE`) + }, 60000); + } + ); - // Listen for messages - socket.addEventListener("message", function (event) { - // updateUsers(message); - console.log(event.data); + Origin.on("location:change", function () { + socket.send(`${user.id}::location::${window.location.hash}`); }); - } - - function updateUsers(message) { - if (allActiveUsers.length === 0) { - allActiveUsers.push(message); - return; - } - const index = allActiveUsers.findIndex((x) => x._id === message._id); - - if (index === undefined || index === -1) { - allActiveUsers.push(message); + Origin.on("editorView:postRender", function () { render(); - return; - } - - allActiveUsers[index] = message; - render(); - } - - function userDataLoaded() { - socket.send(users[0]); - updateUsers(users[0]); - } - - function locationChange() { - if (users.length === 0) return; - - originLocation = Origin.location; - let location = Object.values(originLocation)[3]; - - if (!location) { - location = Object.values(originLocation)[1]; - } - - let currentUser = users[0]; - - Object.assign(currentUser, { - userLocation: location, - lastActive: new Date().toISOString(), - idle: false }); - Origin.once("editorView:render", function () { - userDataLoaded(); + this.socket.addEventListener("message", function (event) { + const data = JSON.parse(event.data); + console.log(data) + render(data) }); } - function checkIdle() { - let currentUser = users[0]; - currentUser.idle = false; - userDataLoaded(); - - loop = setInterval(function () { - if (!currentUser.lastActive) { - currentUser.lastActive = new Date().toISOString(); - } - - const currentDate = new Date(); - const lastAccess = new Date(currentUser.lastActive); - const difference = currentDate - lastAccess; - const minutesDifference = Math.floor(difference / 1000 / 60); + function dataLoaded(currentUser) { + this.socket = new WebSocket(`ws://${serverRoot}/socket`); + + this.user = { + id: currentUser._id, + email: currentUser.email, + userLocation: window.location.hash, + status: 'ACTIVE', + socket: this.socket + }; + + this.socket.onopen = function (e) { + const newUser = JSON.stringify(user) + socket.send(`${user.id}::newUser::${newUser}`); + setupEventListeners(); + }; + } - if (minutesDifference >= idleTime && currentUser.idle !== true) { - currentUser.idle = true; - userDataLoaded(); - } - }, 10000); + function loadData() { + $.get("/api/adaptcollab/getUser/") + .done(function (currentUser) { + dataLoaded(currentUser[0]); + }) + .fail(function (jqXHR, textStatus, errorThrown) { + console.log("user not found"); + }); } }); diff --git a/plugins/adaptcollab/views/adaptCollabView.js b/plugins/adaptcollab/views/adaptCollabView.js index 8a1c245..32c6bca 100644 --- a/plugins/adaptcollab/views/adaptCollabView.js +++ b/plugins/adaptcollab/views/adaptCollabView.js @@ -2,8 +2,6 @@ define(function (require) { var Origin = require("core/origin"); var OriginView = require("core/views/originView"); - const allUsers = []; - let currentLocation = ""; var AdaptCollabView = OriginView.extend( { @@ -15,8 +13,7 @@ define(function (require) { "mouseleave .js-userIcon": "removeUserToolTip" }, - initialize(users, currentUser) { - currentLocation = currentUser.userLocation; + initialize(users) { this.listenToEvents(); this.render(users); }, @@ -56,10 +53,9 @@ define(function (require) { updateUsers(users) { users.forEach((element) => { - if (element.userLocation !== currentLocation) return; - const username = element.email.slice(0, 2).toUpperCase(); element.userName = username; + element.idle = (element.status === 'ACTIVE') ? false : true; var user = $(Handlebars.partials.part_userIcon(element)); this.$(".collab__icon-container").append(user); @@ -67,6 +63,7 @@ define(function (require) { }, render(users) { + console.log(users) $(".toast-container").removeClass("display-none"); var template = Handlebars.templates[this.constructor.template]; this.$el.html(template());