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