summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLuiza Pagliari <lpagliari@gmail.com>2017-04-18 10:21:19 -0300
committerGitHub <noreply@github.com>2017-04-18 10:21:19 -0300
commit8081164a7254f78b9a3dc65408d1188385e977d5 (patch)
treeab904c49856570dd655fdad69eea48b7663ed7eb /src
parent991155c8d4cc419bff276ddf7d52c133f1f076d1 (diff)
parent384697f65396396c821cbd86d3b3be31d385789c (diff)
downloadetherpad-lite-8081164a7254f78b9a3dc65408d1188385e977d5.zip
Merge pull request #3161 from ether/feature/automatic_force_reconnect
Feature: automatic force reconnect
Diffstat (limited to 'src')
-rw-r--r--src/locales/en.json2
-rw-r--r--src/node/handler/PadMessageHandler.js9
-rw-r--r--src/node/utils/Settings.js5
-rw-r--r--src/static/css/pad.css17
-rw-r--r--src/static/js/pad_automatic_reconnect.js182
-rw-r--r--src/static/js/pad_modals.js9
-rw-r--r--src/templates/pad.html16
7 files changed, 226 insertions, 14 deletions
diff --git a/src/locales/en.json b/src/locales/en.json
index 3e16c5de..9e0d49b3 100644
--- a/src/locales/en.json
+++ b/src/locales/en.json
@@ -74,6 +74,8 @@
"pad.modals.connected": "Connected.",
"pad.modals.reconnecting": "Reconnecting to your pad..",
"pad.modals.forcereconnect": "Force reconnect",
+ "pad.modals.reconnecttimer": "Trying to reconnect in ",
+ "pad.modals.cancel": "Cancel",
"pad.modals.userdup": "Opened in another window",
"pad.modals.userdup.explanation": "This pad seems to be opened in more than one browser window on this computer.",
diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js
index 279a44e1..20b262f4 100644
--- a/src/node/handler/PadMessageHandler.js
+++ b/src/node/handler/PadMessageHandler.js
@@ -936,7 +936,7 @@ function handleSwitchToPad(client, message)
var currentSession = sessioninfos[client.id];
var padId = currentSession.padId;
var roomClients = _getRoomClients(padId);
-
+
async.forEach(roomClients, function(client, callback) {
var sinfo = sessioninfos[client.id];
if(sinfo && sinfo.author == currentSession.author) {
@@ -1115,7 +1115,7 @@ function handleClientReady(client, message)
//Check if this author is already on the pad, if yes, kick the other sessions!
var roomClients = _getRoomClients(pad.id);
-
+
async.forEach(roomClients, function(client, callback) {
var sinfo = sessioninfos[client.id];
if(sinfo && sinfo.author == author) {
@@ -1176,6 +1176,7 @@ function handleClientReady(client, message)
"accountPrivs": {
"maxRevisions": 100
},
+ "automaticReconnectionTimeout": settings.automaticReconnectionTimeout,
"initialRevisionList": [],
"initialOptions": {
"guestPolicy": "deny"
@@ -1676,13 +1677,13 @@ function composePadChangesets(padId, startNum, endNum, callback)
function _getRoomClients(padID) {
var roomClients = []; var room = socketio.sockets.adapter.rooms[padID];
-
+
if (room) {
for (var id in room.sockets) {
roomClients.push(socketio.sockets.sockets[id]);
}
}
-
+
return roomClients;
}
diff --git a/src/node/utils/Settings.js b/src/node/utils/Settings.js
index 24bc25c3..a564501d 100644
--- a/src/node/utils/Settings.js
+++ b/src/node/utils/Settings.js
@@ -178,6 +178,11 @@ exports.loglevel = "INFO";
exports.disableIPlogging = false;
/**
+ * Number of seconds to automatically reconnect pad
+ */
+exports.automaticReconnectionTimeout = 0;
+
+/**
* Disable Load Testing
*/
exports.loadTest = false;
diff --git a/src/static/css/pad.css b/src/static/css/pad.css
index 5764c5e4..0b881d78 100644
--- a/src/static/css/pad.css
+++ b/src/static/css/pad.css
@@ -517,6 +517,23 @@ table#otheruserstable {
display: block;
}
+/* styles for the automatic reconnection timer: */
+#connectivity .visible.with_reconnect_timer button,
+#connectivity .visible.with_reconnect_timer .reconnecttimer * {
+ display: inline-block;
+}
+
+#connectivity .with_reconnect_timer .hidden,
+#connectivity .with_reconnect_timer #defaulttext.hidden,
+#connectivity .with_reconnect_timer button.hidden {
+ display: none;
+}
+
+#connectivity .with_reconnect_timer #cancelreconnect {
+ margin-left: 10px;
+}
+/* end of styles for the automatic reconnection timer */
+
#reconnect_form button {
font-size: 12pt;
padding: 5px;
diff --git a/src/static/js/pad_automatic_reconnect.js b/src/static/js/pad_automatic_reconnect.js
new file mode 100644
index 00000000..b5b99bcd
--- /dev/null
+++ b/src/static/js/pad_automatic_reconnect.js
@@ -0,0 +1,182 @@
+
+exports.showCountDownTimerToReconnectOnModal = function($modal, pad) {
+ if (clientVars.automaticReconnectionTimeout && $modal.is('.with_reconnect_timer')) {
+ createCountDownElementsIfNecessary($modal);
+
+ var timer = createTimerForModal($modal, pad);
+
+ $modal.find('#cancelreconnect').one('click', function() {
+ timer.cancel();
+ disableAutomaticReconnection($modal);
+ });
+
+ enableAutomaticReconnection($modal);
+ }
+}
+
+var createCountDownElementsIfNecessary = function($modal) {
+ var elementsDoNotExist = $modal.find('#cancelreconnect').length === 0;
+ if (elementsDoNotExist) {
+ var $defaultMessage = $modal.find('#defaulttext');
+ var $reconnectButton = $modal.find('#forcereconnect');
+
+ // create extra DOM elements, if they don't exist
+ var $reconnectTimerMessage = $('<p class="reconnecttimer"> \
+ <span data-l10n-id="pad.modals.reconnecttimer">Trying to reconnect in </span> \
+ <span class="timetoexpire"></span> \
+ </p>');
+ var $cancelReconnect = $('<button id="cancelreconnect" data-l10n-id="pad.modals.cancel">Cancel</button>');
+
+ localize($reconnectTimerMessage);
+ localize($cancelReconnect);
+
+ $reconnectTimerMessage.insertAfter($defaultMessage);
+ $cancelReconnect.insertAfter($reconnectButton);
+ }
+}
+
+var localize = function($element) {
+ html10n.translateElement(html10n.translations, $element.get(0));
+};
+
+var createTimerForModal = function($modal, pad) {
+ var timeUntilReconnection = clientVars.automaticReconnectionTimeout * reconnectionTries.nextTry();
+ var timer = new CountDownTimer(timeUntilReconnection);
+
+ timer.onTick(function(minutes, seconds) {
+ updateCountDownTimerMessage($modal, minutes, seconds);
+ }).onExpire(function() {
+ var wasANetworkError = $modal.is('.disconnected');
+ if (wasANetworkError) {
+ // cannot simply reconnect, client is having issues to establish connection to server
+ waitUntilClientCanConnectToServerAndThen(function() { forceReconnection($modal); }, pad);
+ } else {
+ forceReconnection($modal);
+ }
+ }).start();
+
+ return timer;
+}
+
+var disableAutomaticReconnection = function($modal) {
+ toggleAutomaticReconnectionOption($modal, true);
+}
+var enableAutomaticReconnection = function($modal) {
+ toggleAutomaticReconnectionOption($modal, false);
+}
+var toggleAutomaticReconnectionOption = function($modal, disableAutomaticReconnect) {
+ $modal.find('#cancelreconnect, .reconnecttimer').toggleClass('hidden', disableAutomaticReconnect);
+ $modal.find('#defaulttext').toggleClass('hidden', !disableAutomaticReconnect);
+}
+
+var waitUntilClientCanConnectToServerAndThen = function(callback, pad) {
+ whenConnectionIsRestablishedWithServer(callback, pad);
+ pad.socket.connect();
+}
+
+var whenConnectionIsRestablishedWithServer = function(callback, pad) {
+ // only add listener for the first try, don't need to add another listener
+ // on every unsuccessful try
+ if (reconnectionTries.counter === 1) {
+ pad.socket.once('connect', callback);
+ }
+}
+
+var forceReconnection = function($modal) {
+ $modal.find('#forcereconnect').click();
+}
+
+var updateCountDownTimerMessage = function($modal, minutes, seconds) {
+ minutes = minutes < 10 ? '0' + minutes : minutes;
+ seconds = seconds < 10 ? '0' + seconds : seconds;
+
+ $modal.find('.timetoexpire').text(minutes + ':' + seconds);
+}
+
+// store number of tries to reconnect to server, in order to increase time to wait
+// until next try
+var reconnectionTries = {
+ counter: 0,
+
+ nextTry: function() {
+ // double the time to try to reconnect on every time reconnection fails
+ var nextCounterFactor = Math.pow(2, this.counter);
+ this.counter++;
+
+ return nextCounterFactor;
+ }
+}
+
+// Timer based on http://stackoverflow.com/a/20618517.
+// duration: how many **seconds** until the timer ends
+// granularity (optional): how many **milliseconds** between each 'tick' of timer. Default: 1000ms (1s)
+var CountDownTimer = function(duration, granularity) {
+ this.duration = duration;
+ this.granularity = granularity || 1000;
+ this.running = false;
+
+ this.onTickCallbacks = [];
+ this.onExpireCallbacks = [];
+}
+
+CountDownTimer.prototype.start = function() {
+ if (this.running) {
+ return;
+ }
+ this.running = true;
+ var start = Date.now(),
+ that = this,
+ diff;
+
+ (function timer() {
+ diff = that.duration - Math.floor((Date.now() - start) / 1000);
+
+ if (diff > 0) {
+ that.timeoutId = setTimeout(timer, that.granularity);
+ that.tick(diff);
+ } else {
+ that.running = false;
+ that.tick(0);
+ that.expire();
+ }
+ }());
+};
+
+CountDownTimer.prototype.tick = function(diff) {
+ var obj = CountDownTimer.parse(diff);
+ this.onTickCallbacks.forEach(function(callback) {
+ callback.call(this, obj.minutes, obj.seconds);
+ }, this);
+}
+CountDownTimer.prototype.expire = function() {
+ this.onExpireCallbacks.forEach(function(callback) {
+ callback.call(this);
+ }, this);
+}
+
+CountDownTimer.prototype.onTick = function(callback) {
+ if (typeof callback === 'function') {
+ this.onTickCallbacks.push(callback);
+ }
+ return this;
+};
+
+CountDownTimer.prototype.onExpire = function(callback) {
+ if (typeof callback === 'function') {
+ this.onExpireCallbacks.push(callback);
+ }
+ return this;
+};
+
+CountDownTimer.prototype.cancel = function() {
+ this.running = false;
+ clearTimeout(this.timeoutId);
+ return this;
+};
+
+CountDownTimer.parse = function(seconds) {
+ return {
+ 'minutes': (seconds / 60) | 0,
+ 'seconds': (seconds % 60) | 0
+ };
+};
diff --git a/src/static/js/pad_modals.js b/src/static/js/pad_modals.js
index 67b03662..2fc621dc 100644
--- a/src/static/js/pad_modals.js
+++ b/src/static/js/pad_modals.js
@@ -1,5 +1,5 @@
/**
- * This code is mostly from the old Etherpad. Please help us to comment this code.
+ * This code is mostly from the old Etherpad. Please help us to comment this code.
* This helps other people to understand this code better and helps them to improve it.
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
*/
@@ -19,8 +19,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
+
var padeditbar = require('./pad_editbar').padeditbar;
+var automaticReconnect = require('./pad_automatic_reconnect');
var padmodals = (function()
{
@@ -35,6 +36,10 @@ var padmodals = (function()
padeditbar.toggleDropDown("none", function() {
$("#connectivity .visible").removeClass('visible');
$("#connectivity ."+messageId).addClass('visible');
+
+ var $modal = $('#connectivity .' + messageId);
+ automaticReconnect.showCountDownTimerToReconnectOnModal($modal, pad);
+
padeditbar.toggleDropDown("connectivity");
});
},
diff --git a/src/templates/pad.html b/src/templates/pad.html
index 3d89f9d0..7c1f1fb1 100644
--- a/src/templates/pad.html
+++ b/src/templates/pad.html
@@ -249,12 +249,12 @@
<div class="userdup">
<h1 data-l10n-id="pad.modals.userdup"></h1>
<h2 data-l10n-id="pad.modals.userdup.explanation"></h2>
- <p data-l10n-id="pad.modals.userdup.advice"></p>
+ <p id="defaulttext" data-l10n-id="pad.modals.userdup.advice"></p>
<button id="forcereconnect" data-l10n-id="pad.modals.forcereconnect"></button>
</div>
<div class="unauth">
<h1 data-l10n-id="pad.modals.unauth"></h1>
- <p data-l10n-id="pad.modals.unauth.explanation"></p>
+ <p id="defaulttext" data-l10n-id="pad.modals.unauth.explanation"></p>
<button id="forcereconnect" data-l10n-id="pad.modals.forcereconnect"></button>
</div>
<div class="looping">
@@ -267,16 +267,16 @@
<h2 data-l10n-id="pad.modals.initsocketfail.explanation"></h2>
<p data-l10n-id="pad.modals.initsocketfail.cause"></p>
</div>
- <div class="slowcommit">
+ <div class="slowcommit with_reconnect_timer">
<h1 data-l10n-id="pad.modals.disconnected"></h1>
<h2 data-l10n-id="pad.modals.slowcommit.explanation"></h2>
- <p data-l10n-id="pad.modals.slowcommit.cause"></p>
+ <p id="defaulttext" data-l10n-id="pad.modals.slowcommit.cause"></p>
<button id="forcereconnect" data-l10n-id="pad.modals.forcereconnect"></button>
</div>
- <div class="badChangeset">
+ <div class="badChangeset with_reconnect_timer">
<h1 data-l10n-id="pad.modals.disconnected"></h1>
<h2 data-l10n-id="pad.modals.badChangeset.explanation"></h2>
- <p data-l10n-id="pad.modals.badChangeset.cause"></p>
+ <p id="defaulttext" data-l10n-id="pad.modals.badChangeset.cause"></p>
<button id="forcereconnect" data-l10n-id="pad.modals.forcereconnect"></button>
</div>
<div class="corruptPad">
@@ -288,11 +288,11 @@
<h1 data-l10n-id="pad.modals.deleted"></h1>
<p data-l10n-id="pad.modals.deleted.explanation"></p>
</div>
- <div class="disconnected">
+ <div class="disconnected with_reconnect_timer">
<% e.begin_block("disconnected"); %>
<h1 data-l10n-id="pad.modals.disconnected"></h1>
<h2 data-l10n-id="pad.modals.disconnected.explanation"></h2>
- <p data-l10n-id="pad.modals.disconnected.cause"></p>
+ <p id="defaulttext" data-l10n-id="pad.modals.disconnected.cause"></p>
<button id="forcereconnect" data-l10n-id="pad.modals.forcereconnect"></button>
<% e.end_block(); %>
</div>