diff --git a/www/common/netflux.js b/www/common/netflux.js index 9633ebfba..fa3680c16 100644 --- a/www/common/netflux.js +++ b/www/common/netflux.js @@ -235,6 +235,18 @@ return /******/ (function(modules) { // webpackBootstrap channel.topologyService.broadcast(channel, protocol.message(cs.USER_DATA, { id: channel.myID, data: data })).then(resolve, reject); }); } + }, { + key: 'sendPing', + value: function sendPing() { + var channel = this; + return new Promise(function (resolve, reject) { + if (channel.channels.size === 0) { + resolve(); + } + var protocol = _ServiceProvider2.default.get(channel.settings.protocol); + channel.topologyService.broadcast(channel, protocol.message(cs.PING, { data: '' })).then(resolve, reject); + }); + } }, { key: 'getHistory', value: function getHistory(historyKeeperID) { @@ -335,6 +347,7 @@ return /******/ (function(modules) { // webpackBootstrap var JOIN_START = exports.JOIN_START = 2; var JOIN_FINISH = exports.JOIN_FINISH = 4; var YOUR_NEW_ID = exports.YOUR_NEW_ID = 5; + var PING = exports.PING = 7; // Internal message to a specific Service var SERVICE_DATA = exports.SERVICE_DATA = 3; @@ -630,7 +643,7 @@ return /******/ (function(modules) { // webpackBootstrap /* 6 */ /***/ function(module, exports) { - "use strict"; + 'use strict'; Object.defineProperty(exports, "__esModule", { value: true @@ -648,7 +661,7 @@ return /******/ (function(modules) { // webpackBootstrap } _createClass(StarTopologyService, [{ - key: "broadcast", + key: 'broadcast', value: function broadcast(webChannel, data) { return new Promise(function (resolve, reject) { var _iteratorNormalCompletion = true; @@ -659,7 +672,14 @@ return /******/ (function(modules) { // webpackBootstrap for (var _iterator = webChannel.channels[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var c = _step.value; - var msg = JSON.stringify([c.seq++, data.type, webChannel.id, data.msg]); + var msg = undefined; + if (data.type === 'PING') { + var date = new Date().getTime(); + // webChannel.lastPing = date; + msg = JSON.stringify([0, 'PING', date]); + } else { + msg = JSON.stringify([c.seq++, data.type, webChannel.id, data.msg]); + } c.send(msg); } } catch (err) { @@ -681,7 +701,7 @@ return /******/ (function(modules) { // webpackBootstrap }); } }, { - key: "sendTo", + key: 'sendTo', value: function sendTo(id, webChannel, data) { return new Promise(function (resolve, reject) { var _iteratorNormalCompletion2 = true; @@ -1178,7 +1198,43 @@ return /******/ (function(modules) { // webpackBootstrap case cs.JOIN_FINISH: webChannel.topologyService.addFinish(webChannel, msg.id); if (msg.id != webChannel.myID) { + // A new user has just registered webChannel.onJoining(msg.id); + } else { + (function () { + // We're fully synced, trigger onJoining for all existing users + var waitForOnJoining = function waitForOnJoining() { + if (typeof webChannel.onJoining !== "function") { + setTimeout(waitForOnJoining, 500); + return; + } + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = webChannel.channels[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var c = _step.value; + + webChannel.onJoining(c.peerID); + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + }; + waitForOnJoining(); + })(); } break; } @@ -1260,12 +1316,14 @@ return /******/ (function(modules) { // webpackBootstrap var webChannel = socket.webChannel; var topology = cs.STAR_SERVICE; var topologyService = _ServiceProvider2.default.get(topology); + var HISTORY_KEEPER = '_HISTORY_KEEPER_'; if (msg[0] !== 0) { return; } if (msg[1] === 'IDENT') { socket.uid = msg[2]; + webChannel.myID = msg[2]; webChannel.peers = []; webChannel.topology = topology; return; @@ -1275,11 +1333,18 @@ return /******/ (function(modules) { // webpackBootstrap socket.send(JSON.stringify(msg)); return; } + if (msg[1] === 'PONG') { + var lag = new Date().getTime() - msg[2]; + webChannel.getLag = function () { + return lag; + }; + return; + } if (msg[2] === 'MSG') {} // We have received a new direct message from another user if (msg[2] === 'MSG' && msg[3] === socket.uid) { // If it comes form the history keeper, send it to the user - if (msg[1] === '_HISTORY_KEEPER_') { + if (msg[1] === HISTORY_KEEPER) { var msgHistory = JSON.parse(msg[4]); webChannel.onmessage(msgHistory[1], msgHistory[4]); } @@ -1298,9 +1363,8 @@ return /******/ (function(modules) { // webpackBootstrap webChannel.onopen(); } else { // Trigger onJoining() when another user is joining the channel - // Register the user in the list of peers in the channel - var linkQuality = msg[1] === '_HISTORY_KEEPER_' ? 1000 : 0; + var linkQuality = msg[1] === HISTORY_KEEPER ? 1000 : 0; var sendToPeer = function sendToPeer(data) { topologyService.sendTo(msg[1], webChannel, { type: 'MSG', msg: data }); }; @@ -1309,7 +1373,17 @@ return /******/ (function(modules) { // webpackBootstrap webChannel.peers.push(peer); } - if (typeof webChannel.onJoining === "function") webChannel.onJoining(msg[1]); + if (msg[1] !== HISTORY_KEEPER) { + // Trigger onJoining with that peer once the function is loaded (i.e. once the channel is synced) + var waitForOnJoining = function waitForOnJoining() { + if (typeof webChannel.onJoining !== "function") { + setTimeout(waitForOnJoining, 500); + return; + } + webChannel.onJoining(msg[1]); + }; + waitForOnJoining(); + } } } // We have received a new message in that channel from another peer @@ -1335,6 +1409,9 @@ return /******/ (function(modules) { // webpackBootstrap case cs.JOIN_START: type = 'JOIN'; break; + case cs.PING: + type = 'PING'; + break; } return { type: type, msg: data.data }; } diff --git a/www/common/realtime-input.js b/www/common/realtime-input.js index 21ab83361..51e83d976 100644 --- a/www/common/realtime-input.js +++ b/www/common/realtime-input.js @@ -184,11 +184,25 @@ define([ } }; + var userList = { + onChange : function() {}, + users: [] + }; var onJoining = function(peer) { + var list = userList.users; + if(list.indexOf(peer) === -1) { + userList.users.push(peer); + } + userList.onChange(); }; var onLeaving = function(peer) { - chainpadAdapter.leaving(peer); + var list = userList.users; + var index = list.indexOf(peer); + if(index !== -1) { + userList.users.splice(index, 1); + } + userList.onChange(); }; chainpadAdapter = { @@ -212,15 +226,17 @@ define([ } }, - msgOut : function(msg) { + msgOut : function(msg, wc) { var parsed = parseMessage(msg); if(parsed.content[0] === 0) { // Someone is registering onMessage('', '1:y'+mkMessage('', channel, [1,0])); - return msg; + // return msg; + return; } if(parsed.content[0] === 4) { // Someone is registering parsed.content[0] = 5; onMessage('', '1:y'+mkMessage(parsed.user, parsed.channelId, parsed.content)); + wc.sendPing(); return; } return Crypto.encrypt(msg, cryptKey); @@ -286,9 +302,13 @@ define([ if(config.onInit) { config.onInit({ - realtime: realtime + realtime: realtime, + webChannel: wc, + userList: userList }); } + // Trigger onJoining with our own Cryptpad username to tell the toolbar that we are synced + onJoining(userName); // we're fully synced initializing = false; @@ -301,7 +321,7 @@ define([ // On sending message realtime.onMessage(function(message) { // Prevent Chainpad from sending authentication messages since it is handled by Netflux - message = chainpadAdapter.msgOut(message); + message = chainpadAdapter.msgOut(message, wc); if(message) { wc.send(message).then(function() { // Send the message back to Chainpad once it is sent to all peers if using the WebRTC protocol diff --git a/www/common/toolbar.js b/www/common/toolbar.js index 4159ea0ac..49b3f2cab 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -123,6 +123,17 @@ define([ return $container.find('#'+id)[0]; }; + var getOtherUsers = function(myUserName, userList) { + var length = userList.length; + var list = (length > 1) ? ' : ' : ''; + userList.forEach(function(user) { + if(user !== myUserName) { + list += user + ', '; + } + }); + return (length > 1) ? list.slice(0, -2) : list; + } + var updateUserList = function (myUserName, listElement, userList) { var meIdx = userList.indexOf(myUserName); if (meIdx === -1) { @@ -132,9 +143,9 @@ define([ if (userList.length === 1) { listElement.textContent = Messages.editingAlone; } else if (userList.length === 2) { - listElement.textContent = Messages.editingWithOneOtherPerson; + listElement.textContent = Messages.editingWithOneOtherPerson + getOtherUsers(myUserName, userList); } else { - listElement.textContent = Messages.editingWith + ' ' + (userList.length - 1) + ' ' + Messages.otherPeople; + listElement.textContent = Messages.editingWith + ' ' + (userList.length - 1) + ' ' + Messages.otherPeople + getOtherUsers(myUserName, userList); } }; @@ -144,14 +155,20 @@ define([ return $container.find('#'+id)[0]; }; - var checkLag = function (realtime, lagElement) { - var lag = realtime.getLag(); - var lagSec = lag.lag/1000; + var checkLag = function (webChannel, lagElement) { + if(typeof webChannel.getLag !== "function") { return; } + var lag = webChannel.getLag(); var lagMsg = Messages.lag + ' '; - if (lag.waiting && lagSec > 1) { - lagMsg += "?? " + Math.floor(lagSec); - } else { - lagMsg += lagSec; + if(lag) { + var lagSec = lag/1000; + if (lag.waiting && lagSec > 1) { + lagMsg += "?? " + Math.floor(lagSec); + } else { + lagMsg += lagSec; + } + } + else { + lagMsg += "??"; } lagElement.textContent = lagMsg; }; @@ -182,7 +199,7 @@ define([ localStorage['CryptPad_RECENTPADS'] = JSON.stringify(out); }; - var create = function ($container, myUserName, realtime) { + var create = function ($container, myUserName, realtime, webChannel, userList) { var toolbar = createRealtimeToolbar($container); createEscape(toolbar.find('.rtwysiwyg-toolbar-leftside')); var userListElement = createUserList(toolbar.find('.rtwysiwyg-toolbar-leftside')); @@ -193,11 +210,12 @@ define([ var connected = false; - realtime.onUserListChange(function (userList) { - if (userList.indexOf(myUserName) !== -1) { connected = true; } - if (!connected) { return; } - updateUserList(myUserName, userListElement, userList); - }); + userList.onChange = function() { + var users = userList.users; + if (users.indexOf(myUserName) !== -1) { connected = true; } + if (!connected) { return; } + updateUserList(myUserName, userListElement, users); + } var ks = function () { if (connected) { kickSpinner(spinner, false); } @@ -209,7 +227,7 @@ define([ setInterval(function () { if (!connected) { return; } - checkLag(realtime, lagElement); + checkLag(webChannel, lagElement); }, 3000); return { diff --git a/www/vdom/main.js b/www/vdom/main.js index 795e058a6..532eba384 100644 --- a/www/vdom/main.js +++ b/www/vdom/main.js @@ -144,7 +144,7 @@ define([ var onInit = function (info) { var $bar = $('#pad-iframe')[0].contentWindow.$('#cke_1_toolbox'); - toolbar = info.realtime.toolbar = Toolbar.create($bar, userName, info.realtime); + toolbar = info.realtime.toolbar = Toolbar.create($bar, userName, info.realtime, info.webChannel, info.userList); /* TODO handle disconnects and such*/ };