Implement degraded mode when too many editors are in a pad

pull/1/head
yflory 4 years ago
parent a5232b6cdb
commit f6015e419e

@ -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

@ -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); },

@ -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'];

@ -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: {}

@ -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

@ -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));

@ -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');

@ -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;
};

@ -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');

@ -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 = '<span class="fa fa-users"></span>';
var fa_viewusers = '<span class="fa fa-eye"></span>';
var fa_viewusers = numberOfViewUsers === '' ? '' : '<span class="fa fa-eye"></span>';
var $spansmall = $('<span>').html(fa_editusers + ' ' + numberOfEditUsers + '&nbsp;&nbsp; ' + 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
$('<em>').text(Messages.toolbar_degraded).appendTo($editUsersList);
return;
}
// Display the userlist
// Editors

@ -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

@ -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) {

Loading…
Cancel
Save