diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js
index 91e5692b1..68e341562 100644
--- a/www/common/cryptpad-common.js
+++ b/www/common/cryptpad-common.js
@@ -1026,13 +1026,6 @@ define([
};
onlyoffice.onEvent = Util.mkEvent();
- // Cursor
- var cursor = common.cursor = {};
- cursor.execCommand = function (data, cb) {
- postMessage("CURSOR_COMMAND", data, cb);
- };
- cursor.onEvent = Util.mkEvent();
-
// Mailbox
var mailbox = common.mailbox = {};
mailbox.execCommand = function (data, cb) {
@@ -2138,8 +2131,6 @@ define([
},
// OnlyOffice
OO_EVENT: common.onlyoffice.onEvent.fire,
- // Cursor
- CURSOR_EVENT: common.cursor.onEvent.fire,
// Mailbox
MAILBOX_EVENT: common.mailbox.onEvent.fire,
// Universal
diff --git a/www/common/metadata-manager.js b/www/common/metadata-manager.js
index da3c665d9..43367edc7 100644
--- a/www/common/metadata-manager.js
+++ b/www/common/metadata-manager.js
@@ -15,6 +15,7 @@ define(['json.sortify'], function (Sortify) {
var priv = {};
var dirty = true;
var history = false;
+ var degraded = true;
var changeHandlers = [];
var lazyChangeHandlers = [];
var titleChangeHandlers = [];
@@ -64,7 +65,7 @@ define(['json.sortify'], function (Sortify) {
if (members.indexOf(x) === -1 && !history) { return; }
mdo[x] = metadataObj.users[x];
});
- if (!priv.readOnly) {
+ if (!priv.readOnly && !degraded) {
mdo[meta.user.netfluxId] = meta.user;
}
metadataObj.users = mdo;
@@ -176,6 +177,10 @@ define(['json.sortify'], function (Sortify) {
getMetadataLazy: function () {
return metadataLazyObj;
},
+ setDegraded: function (bool) {
+ degraded = bool;
+ },
+ isDegraded: function () { return degraded; },
onTitleChange: function (f) { titleChangeHandlers.push(f); },
onChange: function (f) { changeHandlers.push(f); },
onChangeLazy: function (f) { lazyChangeHandlers.push(f); },
diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js
index 13064db8a..bc857be7f 100644
--- a/www/common/outer/async-store.js
+++ b/www/common/outer/async-store.js
@@ -1482,17 +1482,6 @@ define([
}
};
- // Cursor
- Store.cursor = {
- execCommand: function (clientId, data, cb) {
- // The cursor module can only be used when the store is ready
- onReadyEvt.reg(function () {
- if (!store.cursor) { return void cb ({error: 'Cursor channel is disabled'}); }
- store.cursor.execCommand(clientId, data, cb);
- });
- }
- };
-
// Mailbox
Store.mailbox = {
execCommand: function (clientId, data, cb) {
@@ -2335,7 +2324,7 @@ define([
store.messenger.leavePad(chanId);
} catch (e) { console.error(e); }
try {
- store.cursor.leavePad(chanId);
+ store.modules['cursor'].leavePad(chanId);
} catch (e) { console.error(e); }
try {
store.onlyoffice.leavePad(chanId);
@@ -2357,9 +2346,6 @@ define([
if (driveIdx !== -1) {
driveEventClients.splice(driveIdx, 1);
}
- try {
- store.cursor.removeClient(clientId);
- } catch (e) { console.error(e); }
try {
store.onlyoffice.removeClient(clientId);
} catch (e) { console.error(e); }
@@ -2499,17 +2485,6 @@ define([
});
};
*/
- var loadCursor = function () {
- store.cursor = Cursor.init(store, function (ev, data, clients) {
- clients.forEach(function (cId) {
- postMessage(cId, 'CURSOR_EVENT', {
- ev: ev,
- data: data
- });
- });
- });
- };
-
var loadOnlyOffice = function () {
store.onlyoffice = OnlyOffice.init(store, function (ev, data, clients) {
clients.forEach(function (cId) {
@@ -2656,7 +2631,7 @@ define([
};
postMessage(clientId, 'LOADING_DRIVE', data);
});
- loadCursor();
+ loadUniversal(Cursor, 'cursor', waitFor);
loadOnlyOffice();
loadUniversal(Messenger, 'messenger', waitFor);
store.messenger = store.modules['messenger'];
diff --git a/www/common/outer/cursor.js b/www/common/outer/cursor.js
index df1e07353..92c4378cc 100644
--- a/www/common/outer/cursor.js
+++ b/www/common/outer/cursor.js
@@ -6,6 +6,8 @@ define([
], function (Util, Constants, Messages, Crypto) {
var Cursor = {};
+ var DEGRADED = 3; // XXX Number of users before switching to degraded mode
+
var convertToUint8 = function (obj) {
var l = Object.keys(obj).length;
var u = new Uint8Array(l);
@@ -21,6 +23,7 @@ define([
if (!client || !client.cursor) { return; }
var chan = ctx.channels[client.channel];
if (!chan) { return; }
+ if (chan.degraded) { return; }
if (!chan.sendMsg) { return; } // Store not synced yet, we're running with the cache
var data = {
id: client.id,
@@ -34,6 +37,7 @@ define([
// Send all our cursors data when someone remote joins the channel
var sendOurCursors = function (ctx, chan) {
+ if (chan.degraded) { return; }
chan.clients.forEach(function (c) {
var client = ctx.clients[c];
if (!client) { return; }
@@ -77,6 +81,7 @@ define([
// ==> Send the cursor position of the other tabs
chan.clients.forEach(function (cl) {
var clientObj = ctx.clients[cl];
+ if (chan.degraded) { return; }
if (!clientObj) { return; }
ctx.emit('MESSAGE', {
id: clientObj.id,
@@ -90,6 +95,11 @@ define([
return void cb();
}
+ var updateDegraded = function (ctx, wc, chan) {
+ var m = wc.members;
+ chan.degraded = (m.length-1) >= DEGRADED;
+ ctx.emit('DEGRADED', { degraded: chan.degraded }, chan.clients);
+ };
var onOpen = function (wc) {
ctx.channels[channel] = ctx.channels[channel] || {};
@@ -113,11 +123,14 @@ define([
wc.on('join', function () {
sendOurCursors(ctx, chan);
+ updateDegraded(ctx, wc, chan);
});
wc.on('leave', function (peer) {
ctx.emit('MESSAGE', {leave: true, id: peer}, chan.clients);
+ updateDegraded(ctx, wc, chan);
});
wc.on('message', function (cryptMsg) {
+ if (chan.degraded) { return; }
var msg = chan.encryptor.decrypt(cryptMsg, secret.keys && secret.keys.validateKey);
var parsed;
try {
@@ -129,6 +142,7 @@ define([
} catch (e) { console.error(e); }
});
+
chan.wc = wc;
chan.sendMsg = function (msg, cb) {
cb = cb || function () {};
@@ -144,6 +158,8 @@ define([
chan.clients = [client];
first = false;
cb();
+
+ updateDegraded(ctx, wc, chan);
};
network.join(channel).then(onOpen, function (err) {
@@ -223,10 +239,10 @@ define([
delete ctx.clients[clientId];
};
- Cursor.init = function (store, emit) {
+ Cursor.init = function (cfg, waitFor, emit) {
var cursor = {};
var ctx = {
- store: store,
+ store: cfg.store,
emit: emit,
channels: {},
clients: {}
diff --git a/www/common/outer/store-rpc.js b/www/common/outer/store-rpc.js
index 5bae1506f..e444f47f5 100644
--- a/www/common/outer/store-rpc.js
+++ b/www/common/outer/store-rpc.js
@@ -67,8 +67,6 @@ define([
ANON_GET_PREVIEW_CONTENT: Store.anonGetPreviewContent,
// OnlyOffice
OO_COMMAND: Store.onlyoffice.execCommand,
- // Cursor
- CURSOR_COMMAND: Store.cursor.execCommand,
// Mailbox
MAILBOX_COMMAND: Store.mailbox.execCommand,
// Universal
diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js
index 079e77ffc..802bdc341 100644
--- a/www/common/sframe-app-framework.js
+++ b/www/common/sframe-app-framework.js
@@ -579,7 +579,7 @@ define([
common.openPadChat(onLocal);
if (!readOnly && cursorGetter) {
common.openCursorChannel(onLocal);
- cursor = common.createCursor();
+ cursor = common.createCursor(onLocal);
cursor.onCursorUpdate(function (data) {
var newContentStr = cpNfInner.chainpad.getUserDoc();
var hjson = normalize(JSON.parse(newContentStr));
diff --git a/www/common/sframe-common-codemirror.js b/www/common/sframe-common-codemirror.js
index e65a68fb9..970272124 100644
--- a/www/common/sframe-common-codemirror.js
+++ b/www/common/sframe-common-codemirror.js
@@ -530,6 +530,9 @@ define([
}
};
exp.setRemoteCursor = function (data) {
+ if (data.reset) {
+ return void exp.removeCursors();
+ }
if (data.leave) {
$('.cp-codemirror-cursor[id^='+data.id+']').each(function (i, el) {
var id = $(el).attr('id');
diff --git a/www/common/sframe-common-cursor.js b/www/common/sframe-common-cursor.js
index 2e46d3cf1..c71bc40e7 100644
--- a/www/common/sframe-common-cursor.js
+++ b/www/common/sframe-common-cursor.js
@@ -3,28 +3,13 @@ define([
], function (Util) {
var module = {};
- module.create = function (Common) {
+ module.create = function (Common, onLocal) {
var exp = {};
- var sframeChan = Common.getSframeChannel();
var metadataMgr = Common.getMetadataMgr();
var privateData = metadataMgr.getPrivateData();
var share = Util.find(privateData, ['settings', 'general', 'cursor', 'share']);
var show = Util.find(privateData, ['settings', 'general', 'cursor', 'show']);
- var execCommand = function (cmd, data, cb) {
- sframeChan.query('Q_CURSOR_COMMAND', {cmd: cmd, data: data}, function (err, obj) {
- if (err || (obj && obj.error)) { return void cb(err || (obj && obj.error)); }
- cb(void 0, obj);
- });
- };
-
- exp.updateCursor = function (obj) {
- if (share === false) { return; }
- execCommand('UPDATE', obj, function (err) {
- if (err) { console.error(err); }
- });
- };
-
var messageHandlers = [];
exp.onCursorUpdate = function (handler) {
messageHandlers.push(handler);
@@ -40,15 +25,47 @@ define([
});
};
+ var onDegraded = function (data) {
+ if (data.degraded) {
+ // Enter degraded mode
+ onMessage({
+ reset: true
+ });
+ metadataMgr.setDegraded(true);
+ return void metadataMgr.refresh();
+ }
- sframeChan.on('EV_CURSOR_EVENT', function (obj) {
+ setTimeout(function () {
+ metadataMgr.setDegraded(false);
+ metadataMgr.refresh();
+ setTimeout(onLocal);
+ });
+ };
+
+ var onEvent = function (obj) {
var cmd = obj.ev;
var data = obj.data;
+ if (cmd === 'DEGRADED') {
+ onDegraded(data);
+ return;
+ }
if (cmd === 'MESSAGE') {
onMessage(data);
return;
}
+ };
+
+ var module = Common.makeUniversal('cursor', {
+ onEvent: onEvent
});
+ var execCommand = module.execCommand;
+
+ exp.updateCursor = function (obj) {
+ if (share === false) { return; }
+ execCommand('UPDATE', obj, function (err) {
+ if (err) { console.error(err); }
+ });
+ };
return exp;
};
diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js
index 409911693..39a53d9c5 100644
--- a/www/common/sframe-common-outer.js
+++ b/www/common/sframe-common-outer.js
@@ -1613,20 +1613,17 @@ define([
}
sframeChan.on('Q_CURSOR_OPENCHANNEL', function (data, cb) {
- Cryptpad.cursor.execCommand({
- cmd: 'INIT_CURSOR',
+ Cryptpad.universal.execCommand({
+ type: 'cursor',
data: {
- channel: data,
- secret: secret
+ cmd: 'INIT_CURSOR',
+ data: {
+ channel: data,
+ secret: secret
+ }
}
}, cb);
});
- Cryptpad.cursor.onEvent.reg(function (data) {
- sframeChan.event('EV_CURSOR_EVENT', data);
- });
- sframeChan.on('Q_CURSOR_COMMAND', function (data, cb) {
- Cryptpad.cursor.execCommand(data, cb);
- });
Cryptpad.onTimeoutEvent.reg(function () {
sframeChan.event('EV_WORKER_TIMEOUT');
diff --git a/www/common/toolbar.js b/www/common/toolbar.js
index ce17c3fcf..ccb82a2d8 100644
--- a/www/common/toolbar.js
+++ b/www/common/toolbar.js
@@ -167,6 +167,7 @@ MessengerUI, Messages) {
var $userlistContent = toolbar.userlistContent;
var metadataMgr = config.metadataMgr;
+
var online = !forceOffline && metadataMgr.isConnected();
var userData = metadataMgr.getMetadata().users;
var viewers = metadataMgr.getViewers();
@@ -217,13 +218,25 @@ MessengerUI, Messages) {
numberOfViewUsers = '?';
}
+ if (metadataMgr.isDegraded()) {
+ numberOfEditUsers = Math.max(metadataMgr.getChannelMembers().length - 1, 0);
+ numberOfViewUsers = '';
+ }
+
// Update the buttons
var fa_editusers = '';
- var fa_viewusers = '';
+ var fa_viewusers = numberOfViewUsers === '' ? '' : '';
var $spansmall = $('').html(fa_editusers + ' ' + numberOfEditUsers + ' ' + fa_viewusers + ' ' + numberOfViewUsers);
$userButtons.find('.cp-dropdown-button-title').html('').append($spansmall);
if (!online) { return; }
+
+ if (metadataMgr.isDegraded()) {
+ Messages.toolbar_degraded = "Too many editors are present in the pad. The userlist has been disabled to improve performances"; // XXX
+ $('').text(Messages.toolbar_degraded).appendTo($editUsersList);
+ return;
+ }
+
// Display the userlist
// Editors
diff --git a/www/kanban/inner.js b/www/kanban/inner.js
index 26aa6325b..3428bd12b 100644
--- a/www/kanban/inner.js
+++ b/www/kanban/inner.js
@@ -1247,6 +1247,16 @@ define([
});
framework.onCursorUpdate(function (data) {
if (!data) { return; }
+ if (data.reset) {
+ Object.keys(remoteCursors).forEach(function (id) {
+ if (remoteCursors[id].clear) {
+ remoteCursors[id].clear();
+ }
+ delete remoteCursors[id];
+ });
+ return;
+ }
+
var id = data.id;
// Clear existing cursor
diff --git a/www/pad/cursor.js b/www/pad/cursor.js
index e8753ed4b..42569838f 100644
--- a/www/pad/cursor.js
+++ b/www/pad/cursor.js
@@ -122,6 +122,9 @@ define([
};
exp.onCursorUpdate = function (data, hjson) {
+ if (data.reset) {
+ return void exp.removeCursors(inner);
+ }
if (data.leave) {
if (data.id.length === 32) {
Object.keys(cursors).forEach(function (id) {