Skip to content

Commit

Permalink
Data manipulation moved to server side.
Browse files Browse the repository at this point in the history
  • Loading branch information
joe-allen-89 committed Feb 10, 2023
1 parent 7124df2 commit 156ed0a
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 146 deletions.
112 changes: 72 additions & 40 deletions lib/CollaborationModule.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,27 @@
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
* @extends {AbstractModule}
*/
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();

Expand All @@ -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");
Expand Down Expand Up @@ -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) {
Expand Down
154 changes: 54 additions & 100 deletions plugins/adaptcollab/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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");
});
}
});
9 changes: 3 additions & 6 deletions plugins/adaptcollab/views/adaptCollabView.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
{
Expand All @@ -15,8 +13,7 @@ define(function (require) {
"mouseleave .js-userIcon": "removeUserToolTip"
},

initialize(users, currentUser) {
currentLocation = currentUser.userLocation;
initialize(users) {
this.listenToEvents();
this.render(users);
},
Expand Down Expand Up @@ -56,17 +53,17 @@ 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);
});
},

render(users) {
console.log(users)
$(".toast-container").removeClass("display-none");
var template = Handlebars.templates[this.constructor.template];
this.$el.html(template());
Expand Down

0 comments on commit 156ed0a

Please sign in to comment.