feat: dynamic frontend configuration
This commit is contained in:
parent
9fda0a2c4b
commit
b0337d9f5b
@ -11,7 +11,11 @@ backend:
|
||||
frontend:
|
||||
# When an editable whiteboard is loading in a client,
|
||||
# should it be started in read-only mode.
|
||||
setReadOnlyOnWhiteboardLoad: false
|
||||
readOnlyOnWhiteboardLoad: false
|
||||
# Show smallest screen indicator
|
||||
showSmallestScreenIndicator: true
|
||||
# performance:
|
||||
performance:
|
||||
pointerEventsThreshold:
|
||||
minDistDelta: 1
|
||||
minTimeDelta: 33
|
||||
refreshInfoFreq: 5
|
||||
|
@ -30,13 +30,39 @@
|
||||
"frontend": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["setReadOnlyOnWhiteboardLoad", "showSmallestScreenIndicator"],
|
||||
"required": ["readOnlyOnWhiteboardLoad", "showSmallestScreenIndicator", "performance"],
|
||||
"properties": {
|
||||
"setReadOnlyOnWhiteboardLoad": {
|
||||
"readOnlyOnWhiteboardLoad": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"showSmallestScreenIndicator": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"performance": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["pointerEventsThreshold", "refreshInfoFreq"],
|
||||
"properties": {
|
||||
"pointerEventsThreshold": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["minDistDelta", "minTimeDelta"],
|
||||
"properties": {
|
||||
"minDistDelta": {
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"minTimeDelta": {
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"refreshInfoFreq": {
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -195,19 +195,19 @@ function startBackendServer(port) {
|
||||
var whiteboardId = null;
|
||||
socket.on("disconnect", function () {
|
||||
if (infoByWhiteboard.has(whiteboardId)) {
|
||||
const whiteboardServerSideInfo = infoByWhiteboard.get(whiteboardId);
|
||||
const whiteboardServerSideInfo = infoByWhiteboard.get(whiteboardId);
|
||||
|
||||
if (socket && socket.id) {
|
||||
whiteboardServerSideInfo.deleteScreenResolutionOfClient(socket.id);
|
||||
}
|
||||
if (socket && socket.id) {
|
||||
whiteboardServerSideInfo.deleteScreenResolutionOfClient(socket.id);
|
||||
}
|
||||
|
||||
whiteboardServerSideInfo.decrementNbConnectedUsers();
|
||||
whiteboardServerSideInfo.decrementNbConnectedUsers();
|
||||
|
||||
if (whiteboardServerSideInfo.hasConnectedUser()) {
|
||||
socket.compress(false).broadcast.emit("refreshUserBadges", null); //Removes old user Badges
|
||||
} else {
|
||||
infoByWhiteboard.delete(whiteboardId);
|
||||
}
|
||||
if (whiteboardServerSideInfo.hasConnectedUser()) {
|
||||
socket.compress(false).broadcast.emit("refreshUserBadges", null); //Removes old user Badges
|
||||
} else {
|
||||
infoByWhiteboard.delete(whiteboardId);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -224,6 +224,8 @@ function startBackendServer(port) {
|
||||
socket.on("joinWhiteboard", function (content) {
|
||||
content = escapeAllContentStrings(content);
|
||||
if (accessToken === "" || accessToken == content["at"]) {
|
||||
socket.emit("whiteboardConfig", { common: config.frontend });
|
||||
|
||||
whiteboardId = content["wid"];
|
||||
socket.join(whiteboardId); //Joins room name=wid
|
||||
if (!infoByWhiteboard.has(whiteboardId)) {
|
||||
|
@ -31,11 +31,11 @@ test("Complex object config override", () => {
|
||||
|
||||
test("Override default config", () => {
|
||||
const defaultConfig = getDefaultConfig();
|
||||
const overrideConfig1 = { frontend: { setReadOnlyOnWhiteboardLoad: true } };
|
||||
const overrideConfig1 = { frontend: { readOnlyOnWhiteboardLoad: true } };
|
||||
|
||||
expect(
|
||||
deepMergeConfigs(defaultConfig, overrideConfig1).frontend.setReadOnlyOnWhiteboardLoad
|
||||
).toBe(true);
|
||||
expect(deepMergeConfigs(defaultConfig, overrideConfig1).frontend.readOnlyOnWhiteboardLoad).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
test("Dumb config is not valid", () => {
|
||||
|
@ -1,3 +0,0 @@
|
||||
export const POINTER_EVENT_THRESHOLD_MIN_DIST_DELTA = 1; // 1px
|
||||
export const POINTER_EVENT_THRESHOLD_MIN_TIME_DELTA = 10; // 1ms
|
||||
export const REFRESH_INFO_FREQUENCY = 5; // 5 times per second
|
@ -8,47 +8,48 @@ import pdfjsLib from "pdfjs-dist/webpack";
|
||||
import shortcutFunctions from "./shortcutFunctions";
|
||||
import ReadOnlyService from "./services/ReadOnlyService";
|
||||
import InfoService from "./services/InfoService";
|
||||
import { getQueryVariable, getSubDir } from "./utils";
|
||||
import ConfigService from "./services/ConfigService";
|
||||
|
||||
let whiteboardId = getQueryVariable("whiteboardid");
|
||||
const randomid = getQueryVariable("randomid");
|
||||
if (randomid && !whiteboardId) {
|
||||
//set random whiteboard on empty whiteboardid
|
||||
whiteboardId = Array(2)
|
||||
.fill(null)
|
||||
.map(() => Math.random().toString(36).substr(2))
|
||||
.join("");
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
urlParams.set("whiteboardid", whiteboardId);
|
||||
window.location.search = urlParams;
|
||||
}
|
||||
|
||||
whiteboardId = whiteboardId || "myNewWhiteboard";
|
||||
whiteboardId = unescape(encodeURIComponent(whiteboardId)).replace(/[^a-zA-Z0-9 ]/g, "");
|
||||
const myUsername = getQueryVariable("username") || "unknown" + (Math.random() + "").substring(2, 6);
|
||||
const accessToken = getQueryVariable("accesstoken") || "";
|
||||
|
||||
// Custom Html Title
|
||||
const title = getQueryVariable("title");
|
||||
if (!title === false) {
|
||||
document.title = decodeURIComponent(title);
|
||||
}
|
||||
|
||||
const subdir = getSubDir();
|
||||
let signaling_socket;
|
||||
|
||||
function main() {
|
||||
var whiteboardId = getQueryVariable("whiteboardid");
|
||||
var randomid = getQueryVariable("randomid");
|
||||
if (randomid && !whiteboardId) {
|
||||
//set random whiteboard on empty whiteboardid
|
||||
whiteboardId = Array(2)
|
||||
.fill(null)
|
||||
.map(() => Math.random().toString(36).substr(2))
|
||||
.join("");
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
urlParams.set("whiteboardid", whiteboardId);
|
||||
window.location.search = urlParams;
|
||||
}
|
||||
|
||||
whiteboardId = whiteboardId || "myNewWhiteboard";
|
||||
whiteboardId = unescape(encodeURIComponent(whiteboardId)).replace(/[^a-zA-Z0-9 ]/g, "");
|
||||
var myUsername = getQueryVariable("username");
|
||||
var accessToken = getQueryVariable("accesstoken");
|
||||
myUsername = myUsername || "unknown" + (Math.random() + "").substring(2, 6);
|
||||
accessToken = accessToken || "";
|
||||
var accessDenied = false;
|
||||
|
||||
// Custom Html Title
|
||||
var title = getQueryVariable("title");
|
||||
if (!title === false) {
|
||||
document.title = decodeURIComponent(title);
|
||||
}
|
||||
|
||||
var url = document.URL.substr(0, document.URL.lastIndexOf("/"));
|
||||
var signaling_socket = null;
|
||||
var urlSplit = url.split("/");
|
||||
var subdir = "";
|
||||
for (var i = 3; i < urlSplit.length; i++) {
|
||||
subdir = subdir + "/" + urlSplit[i];
|
||||
}
|
||||
signaling_socket = io("", { path: subdir + "/ws-api" }); // Connect even if we are in a subdir behind a reverse proxy
|
||||
|
||||
signaling_socket.on("connect", function () {
|
||||
console.log("Websocket connected!");
|
||||
|
||||
signaling_socket.on("whiteboardConfig", (serverResponse) => {
|
||||
ConfigService.initFromServer(serverResponse);
|
||||
// Inti whiteboard only when we have the config from the server
|
||||
initWhiteboard();
|
||||
});
|
||||
|
||||
signaling_socket.on("whiteboardInfoUpdate", (info) => {
|
||||
InfoService.updateInfoFromServer(info);
|
||||
whiteboard.updateSmallestScreenResolution();
|
||||
@ -63,6 +64,7 @@ function main() {
|
||||
whiteboard.refreshUserBadges();
|
||||
});
|
||||
|
||||
let accessDenied = false;
|
||||
signaling_socket.on("wrongAccessToken", function () {
|
||||
if (!accessDenied) {
|
||||
accessDenied = true;
|
||||
@ -76,7 +78,9 @@ function main() {
|
||||
windowWidthHeight: { w: $(window).width(), h: $(window).height() },
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function initWhiteboard() {
|
||||
$(document).ready(function () {
|
||||
// by default set in readOnly mode
|
||||
ReadOnlyService.activateReadOnlyMode();
|
||||
@ -599,7 +603,7 @@ function main() {
|
||||
// fix bug cursor not showing up
|
||||
whiteboard.refreshCursorAppearance();
|
||||
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
if (process.env.NODE_ENV === "production" && ConfigService.readOnlyOnWhiteboardLoad) {
|
||||
ReadOnlyService.activateReadOnlyMode();
|
||||
InfoService.hideInfo();
|
||||
} else {
|
||||
@ -795,19 +799,6 @@ function main() {
|
||||
}, 1000 * options.hideAfter);
|
||||
}
|
||||
}
|
||||
|
||||
// get 'GET' parameter by variable name
|
||||
function getQueryVariable(variable) {
|
||||
var query = window.location.search.substring(1);
|
||||
var vars = query.split("&");
|
||||
for (var i = 0; i < vars.length; i++) {
|
||||
var pair = vars[i].split("=");
|
||||
if (pair[0] == variable) {
|
||||
return pair[1];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export default main;
|
||||
|
51
src/js/services/ConfigService.js
Normal file
51
src/js/services/ConfigService.js
Normal file
@ -0,0 +1,51 @@
|
||||
class ConfigService {
|
||||
/**
|
||||
* @readonly
|
||||
* @type {boolean}
|
||||
*/
|
||||
readOnlyOnWhiteboardLoad = false;
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
* @type {boolean}
|
||||
*/
|
||||
showSmallestScreenIndicator = true;
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
* @type {number}
|
||||
*/
|
||||
pointerEventsThresholdMinDistDelta = 0;
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
* @type {number}
|
||||
*/
|
||||
pointerEventsThresholdMinTimeDelta = 0;
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
* @type {number}
|
||||
*/
|
||||
refreshInfoInterval = 1000;
|
||||
|
||||
/**
|
||||
* Init the service from the config sent by the server
|
||||
*
|
||||
* @param {object} serverResponse
|
||||
*/
|
||||
initFromServer(serverResponse) {
|
||||
const { common } = serverResponse;
|
||||
const { readOnlyOnWhiteboardLoad, showSmallestScreenIndicator, performance } = common;
|
||||
|
||||
this.readOnlyOnWhiteboardLoad = readOnlyOnWhiteboardLoad;
|
||||
this.showSmallestScreenIndicator = showSmallestScreenIndicator;
|
||||
this.pointerEventsThresholdMinDistDelta = performance.pointerEventsThreshold.minDistDelta;
|
||||
this.pointerEventsThresholdMinTimeDelta = performance.pointerEventsThreshold.minTimeDelta;
|
||||
this.refreshInfoInterval = 1000 / performance.refreshInfoFreq;
|
||||
|
||||
console.log("Whiteboard config from server:", serverResponse, "parsed:", this);
|
||||
}
|
||||
}
|
||||
|
||||
export default new ConfigService();
|
@ -1,6 +1,4 @@
|
||||
import { REFRESH_INFO_FREQUENCY } from "../const";
|
||||
|
||||
const REFRESH_INTERVAL = 1000 / REFRESH_INFO_FREQUENCY;
|
||||
import ConfigService from "./ConfigService";
|
||||
|
||||
/**
|
||||
* Class the handle the information about the whiteboard
|
||||
@ -90,7 +88,7 @@ class InfoService {
|
||||
// refresh only on a specific interval to reduce
|
||||
// refreshing cost
|
||||
this.refreshDisplayedInfo();
|
||||
}, REFRESH_INTERVAL);
|
||||
}, ConfigService.refreshInfoInterval);
|
||||
}
|
||||
|
||||
hideInfo() {
|
||||
|
@ -14,3 +14,31 @@ export function computeDist(p1, p2) {
|
||||
export function getCurrentTimeMs() {
|
||||
return new Date().getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* get 'GET' parameter by variable name
|
||||
* @param variable
|
||||
* @return {boolean|*}
|
||||
*/
|
||||
export function getQueryVariable(variable) {
|
||||
const query = window.location.search.substring(1);
|
||||
const vars = query.split("&");
|
||||
for (let i = 0; i < vars.length; i++) {
|
||||
const pair = vars[i].split("=");
|
||||
if (pair[0] === variable) {
|
||||
return pair[1];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getSubDir() {
|
||||
const url = document.URL.substr(0, document.URL.lastIndexOf("/"));
|
||||
const urlSplit = url.split("/");
|
||||
let subdir = "";
|
||||
for (let i = 3; i < urlSplit.length; i++) {
|
||||
subdir = subdir + "/" + urlSplit[i];
|
||||
}
|
||||
|
||||
return subdir;
|
||||
}
|
||||
|
@ -1,12 +1,9 @@
|
||||
import { dom } from "@fortawesome/fontawesome-svg-core";
|
||||
import { getCurrentTimeMs } from "./utils";
|
||||
import Point from "./classes/Point";
|
||||
import {
|
||||
POINTER_EVENT_THRESHOLD_MIN_DIST_DELTA,
|
||||
POINTER_EVENT_THRESHOLD_MIN_TIME_DELTA,
|
||||
} from "./const";
|
||||
import ReadOnlyService from "./services/ReadOnlyService";
|
||||
import InfoService from "./services/InfoService";
|
||||
import ConfigService from "./services/ConfigService";
|
||||
|
||||
const RAD_TO_DEG = 180.0 / Math.PI;
|
||||
const DEG_TO_RAD = Math.PI / 180.0;
|
||||
@ -215,11 +212,11 @@ const whiteboard = {
|
||||
const pointerSentTime = getCurrentTimeMs();
|
||||
if (
|
||||
pointerSentTime - _this.lastPointerSentTime >
|
||||
POINTER_EVENT_THRESHOLD_MIN_TIME_DELTA
|
||||
ConfigService.pointerEventsThresholdMinTimeDelta
|
||||
) {
|
||||
if (
|
||||
_this.lastPointerPosition.distTo(currentPos) >
|
||||
POINTER_EVENT_THRESHOLD_MIN_DIST_DELTA
|
||||
ConfigService.pointerEventsThresholdMinDistDelta
|
||||
) {
|
||||
_this.lastPointerSentTime = pointerSentTime;
|
||||
_this.lastPointerPosition = currentPos;
|
||||
@ -545,11 +542,14 @@ const whiteboard = {
|
||||
});
|
||||
|
||||
const pointerSentTime = getCurrentTimeMs();
|
||||
if (pointerSentTime - _this.lastPointerSentTime > POINTER_EVENT_THRESHOLD_MIN_TIME_DELTA) {
|
||||
if (
|
||||
pointerSentTime - _this.lastPointerSentTime >
|
||||
ConfigService.pointerEventsThresholdMinTimeDelta
|
||||
) {
|
||||
const newPointerPosition = currentPos;
|
||||
if (
|
||||
_this.lastPointerPosition.distTo(newPointerPosition) >
|
||||
POINTER_EVENT_THRESHOLD_MIN_DIST_DELTA
|
||||
ConfigService.pointerEventsThresholdMinDistDelta
|
||||
) {
|
||||
_this.lastPointerSentTime = pointerSentTime;
|
||||
_this.lastPointerPosition = newPointerPosition;
|
||||
@ -882,12 +882,12 @@ const whiteboard = {
|
||||
// At least 100 ms between messages to reduce server load
|
||||
if (
|
||||
pointerSentTime - _this.lastPointerSentTime >
|
||||
POINTER_EVENT_THRESHOLD_MIN_TIME_DELTA
|
||||
ConfigService.pointerEventsThresholdMinTimeDelta
|
||||
) {
|
||||
// Minimal distance between messages to reduce server load
|
||||
if (
|
||||
_this.lastPointerPosition.distTo(newPointerPosition) >
|
||||
POINTER_EVENT_THRESHOLD_MIN_DIST_DELTA
|
||||
ConfigService.pointerEventsThresholdMinDistDelta
|
||||
) {
|
||||
_this.lastPointerSentTime = pointerSentTime;
|
||||
_this.lastPointerPosition = newPointerPosition;
|
||||
|
Loading…
Reference in New Issue
Block a user