diff --git a/scripts/server-backend.js b/scripts/server-backend.js index fc9c653..656cf05 100644 --- a/scripts/server-backend.js +++ b/scripts/server-backend.js @@ -185,7 +185,7 @@ function startBackendServer(port) { io.on("connection", function (socket) { let whiteboardId = null; socket.on("disconnect", function () { - WhiteboardInfoBackendService.disconnect(socket.id, whiteboardId); + WhiteboardInfoBackendService.leave(socket.id, whiteboardId); socket.compress(false).broadcast.to(whiteboardId).emit("refreshUserBadges", null); //Removes old user Badges }); diff --git a/scripts/services/WhiteboardInfoBackendService.js b/scripts/services/WhiteboardInfoBackendService.js index 98d17d3..2dc672c 100644 --- a/scripts/services/WhiteboardInfoBackendService.js +++ b/scripts/services/WhiteboardInfoBackendService.js @@ -1,4 +1,5 @@ const config = require("../config/config"); +const ReadOnlyBackendService = require("./ReadOnlyBackendService"); /** * Class to hold information related to a whiteboard @@ -103,11 +104,36 @@ class WhiteboardInfo { } } +/** + * Wrapper class around map to treat both the editable whiteboard and its read-only version the same + */ +class InfoByWhiteBoardMap extends Map { + get(wid) { + const readOnlyId = ReadOnlyBackendService.getReadOnlyId(wid); + return super.get(readOnlyId); + } + + set(wid, val) { + const readOnlyId = ReadOnlyBackendService.getReadOnlyId(wid); + return super.set(readOnlyId, val); + } + + has(wid) { + const readOnlyId = ReadOnlyBackendService.getReadOnlyId(wid); + return super.has(readOnlyId); + } + + delete(wid) { + const readOnlyId = ReadOnlyBackendService.getReadOnlyId(wid); + return super.delete(readOnlyId); + } +} + class WhiteboardInfoBackendService { /** * @type {Map} */ - #infoByWhiteboard = new Map(); + #infoByWhiteboard = new InfoByWhiteBoardMap(); /** * Start the auto sending of information to all the whiteboards @@ -117,12 +143,21 @@ class WhiteboardInfoBackendService { start(io) { // auto clean infoByWhiteboard setInterval(() => { - this.#infoByWhiteboard.forEach((info, whiteboardId) => { + this.#infoByWhiteboard.forEach((info, readOnlyWhiteboardId) => { if (info.shouldSendInfo()) { + // broadcast to editable whiteboard + const wid = ReadOnlyBackendService.getIdFromReadOnlyId(readOnlyWhiteboardId); io.sockets - .in(whiteboardId) + .in(wid) .compress(false) .emit("whiteboardInfoUpdate", info.asObject()); + + // also send to readonly whiteboard + io.sockets + .in(readOnlyWhiteboardId) + .compress(false) + .emit("whiteboardInfoUpdate", info.asObject()); + info.infoWasSent(); } }); @@ -171,7 +206,7 @@ class WhiteboardInfoBackendService { * @param {string} clientId * @param {string} whiteboardId */ - disconnect(clientId, whiteboardId) { + leave(clientId, whiteboardId) { const infoByWhiteboard = this.#infoByWhiteboard; if (infoByWhiteboard.has(whiteboardId)) { @@ -189,6 +224,20 @@ class WhiteboardInfoBackendService { } } } + + /** + * Get the number of clients on a whiteboard + * + * @param {string} wid + * @returns number|null + */ + getNbClientOnWhiteboard(wid) { + const infoByWhiteboard = this.#infoByWhiteboard; + const info = infoByWhiteboard.get(wid); + + if (info) return info.nbConnectedUsers; + else return null; + } } module.exports = new WhiteboardInfoBackendService(); diff --git a/scripts/services/WhiteboardInfoBackendService.test.js b/scripts/services/WhiteboardInfoBackendService.test.js new file mode 100644 index 0000000..25c599c --- /dev/null +++ b/scripts/services/WhiteboardInfoBackendService.test.js @@ -0,0 +1,44 @@ +const ReadOnlyBackendService = require("./ReadOnlyBackendService"); +const WhiteboardInfoBackendService = require("./WhiteboardInfoBackendService"); + +test("Clients lifetime same wid", () => { + const wid = "1"; + expect(WhiteboardInfoBackendService.getNbClientOnWhiteboard(wid)).toBe(null); + + WhiteboardInfoBackendService.join("toto", wid, null); + expect(WhiteboardInfoBackendService.getNbClientOnWhiteboard(wid)).toBe(1); + + WhiteboardInfoBackendService.join("tata", wid, null); + expect(WhiteboardInfoBackendService.getNbClientOnWhiteboard(wid)).toBe(2); + + WhiteboardInfoBackendService.leave("tata", wid, null); + expect(WhiteboardInfoBackendService.getNbClientOnWhiteboard(wid)).toBe(1); + + WhiteboardInfoBackendService.leave("toto", wid, null); + // no more user on whiteboard + expect(WhiteboardInfoBackendService.getNbClientOnWhiteboard(wid)).toBe(null); +}); + +test("Clients lifetime both wid and readonly wid", () => { + const wid = "2"; + const readOnlyWid = ReadOnlyBackendService.getReadOnlyId(wid); + expect(WhiteboardInfoBackendService.getNbClientOnWhiteboard(wid)).toBe(null); + expect(WhiteboardInfoBackendService.getNbClientOnWhiteboard(readOnlyWid)).toBe(null); + + WhiteboardInfoBackendService.join("toto", wid, null); + expect(WhiteboardInfoBackendService.getNbClientOnWhiteboard(wid)).toBe(1); + expect(WhiteboardInfoBackendService.getNbClientOnWhiteboard(readOnlyWid)).toBe(1); + + WhiteboardInfoBackendService.join("tata", readOnlyWid, null); + expect(WhiteboardInfoBackendService.getNbClientOnWhiteboard(wid)).toBe(2); + expect(WhiteboardInfoBackendService.getNbClientOnWhiteboard(readOnlyWid)).toBe(2); + + WhiteboardInfoBackendService.leave("tata", readOnlyWid, null); + expect(WhiteboardInfoBackendService.getNbClientOnWhiteboard(wid)).toBe(1); + expect(WhiteboardInfoBackendService.getNbClientOnWhiteboard(readOnlyWid)).toBe(1); + + WhiteboardInfoBackendService.leave("toto", wid, null); + // no more user on whiteboard + expect(WhiteboardInfoBackendService.getNbClientOnWhiteboard(wid)).toBe(null); + expect(WhiteboardInfoBackendService.getNbClientOnWhiteboard(readOnlyWid)).toBe(null); +});