From 7334173b4a7f9115bc944f9bcde2c80e32d031a8 Mon Sep 17 00:00:00 2001 From: Caleb James DeLisle Date: Mon, 11 Sep 2017 15:46:21 +0200 Subject: [PATCH] Hopefully fix infinite spinner whenever there is a disconnect (pad) --- www/common/common-realtime.js | 47 +++++++++++---------- www/common/common-util.js | 22 ++++++++++ www/common/cryptpad-common.js | 4 ++ www/common/sframe-chainpad-netflux-inner.js | 40 +++++++++++++++++- www/common/sframe-common-outer.js | 8 ++++ www/common/sframe-common.js | 12 +++++- www/common/sframe-protocol.js | 3 ++ www/common/toolbar2.js | 4 ++ www/common/toolbar3.js | 16 +++---- www/pad/inner.js | 15 ++++--- 10 files changed, 128 insertions(+), 43 deletions(-) diff --git a/www/common/common-realtime.js b/www/common/common-realtime.js index c9dd74b29..ab7d0abfb 100644 --- a/www/common/common-realtime.js +++ b/www/common/common-realtime.js @@ -19,38 +19,39 @@ define([ if (typeof(realtime.getAuthDoc) !== 'function') { return void console.error('improper use of this function'); } - window.setTimeout(function () { if (realtime.getAuthDoc() === realtime.getUserDoc()) { return void cb(); } else { realtime.onSettle(cb); } - - if (intr) { return; } - intr = window.setInterval(function () { - var l; - try { - l = realtime.getLag(); - } catch (e) { - throw new Error("ChainPad.getLag() does not exist, please `bower update`"); - } - if (l.lag < BAD_STATE_TIMEOUT || !connected) { return; } - realtime.abort(); - // don't launch more than one popup - if (common.infiniteSpinnerDetected) { return; } - infiniteSpinnerHandlers.forEach(function (ish) { ish(); }); - - // inform the user their session is in a bad state - Cryptpad.confirm(Messages.realtime_unrecoverableError, function (yes) { - if (!yes) { return; } - window.parent.location.reload(); - }); - common.infiniteSpinnerDetected = true; - }, 2000); }, 0); }; + common.beginDetectingInfiniteSpinner = function (Cryptpad, realtime) { + if (intr) { return; } + intr = window.setInterval(function () { + var l; + try { + l = realtime.getLag(); + } catch (e) { + throw new Error("ChainPad.getLag() does not exist, please `bower update`"); + } + if (l.lag < BAD_STATE_TIMEOUT || !connected) { return; } + realtime.abort(); + // don't launch more than one popup + if (common.infiniteSpinnerDetected) { return; } + infiniteSpinnerHandlers.forEach(function (ish) { ish(); }); + + // inform the user their session is in a bad state + Cryptpad.confirm(Messages.realtime_unrecoverableError, function (yes) { + if (!yes) { return; } + window.parent.location.reload(); + }); + common.infiniteSpinnerDetected = true; + }, 2000); + }; + common.onInfiniteSpinner = function (f) { infiniteSpinnerHandlers.push(f); }; common.setConnectionState = function (bool) { diff --git a/www/common/common-util.js b/www/common/common-util.js index e42a0249c..aed4dd5d2 100644 --- a/www/common/common-util.js +++ b/www/common/common-util.js @@ -1,6 +1,28 @@ define([], function () { var Util = {}; + // If once is true, after the event has been fired, any further handlers which are + // registered will fire immediately, and this type of event cannot be fired twice. + Util.mkEvent = function (once) { + var handlers = []; + var fired = false; + return { + reg: function (cb) { + if (once && fired) { return void setTimeout(cb); } + handlers.push(cb); + }, + unreg: function (cb) { + if (handlers.indexOf(cb) === -1) { throw new Error("Not registered"); } + handlers.splice(handlers.indexOf(cb), 1); + }, + fire: function () { + if (fired) { return; } + fired = true; + handlers.forEach(function (h) { h(); }); + } + }; + }; + Util.find = function (map, path) { return (map && path.reduce(function (p, n) { return typeof(p[n]) !== 'undefined' && p[n]; diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 40dbee37a..a9e85fa2f 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -138,6 +138,10 @@ define([ Realtime.whenRealtimeSyncs(common, realtime, cb); }; + common.beginDetectingInfiniteSpinner = function (realtime) { + Realtime.beginDetectingInfiniteSpinner(common, realtime); + }; + // Userlist common.createUserList = UserList.create; diff --git a/www/common/sframe-chainpad-netflux-inner.js b/www/common/sframe-chainpad-netflux-inner.js index 04659fe17..787da1ca3 100644 --- a/www/common/sframe-chainpad-netflux-inner.js +++ b/www/common/sframe-chainpad-netflux-inner.js @@ -15,11 +15,16 @@ * along with this program. If not, see . */ define([ + '/common/common-util.js', + '/customize/application_config.js', '/bower_components/chainpad/chainpad.dist.js' -], function () { +], function (Util, AppConfig) { var ChainPad = window.ChainPad; var module = { exports: {} }; + var badStateTimeout = typeof(AppConfig.badStateTimeout) === 'number' ? + AppConfig.badStateTimeout : 30000; + var verbose = function (x) { console.log(x); }; verbose = function () {}; // comment out to enable verbose logging @@ -44,9 +49,25 @@ define([ var chainpad; var myID; var isReady = false; + var evConnected = Util.mkEvent(true); + var evInfiniteSpinner = Util.mkEvent(true); + + window.setInterval(function () { + if (!chainpad || !myID) { return; } + var l; + try { + l = chainpad.getLag(); + } catch (e) { + throw new Error("ChainPad.getLag() does not exist, please `bower update`"); + } + if (l.lag < badStateTimeout) { return; } + chainpad.abort(); + evInfiniteSpinner.fire(); + }, 2000); sframeChan.on('EV_RT_DISCONNECT', function () { isReady = false; + if (chainpad) { chainpad.abort(); } onConnectionChange({ state: false }); }); sframeChan.on('EV_RT_CONNECT', function (content) { @@ -55,6 +76,7 @@ define([ isReady = false; if (chainpad) { // it's a reconnect + if (chainpad) { chainpad.start(); } onConnectionChange({ state: true, myId: myID }); return; } @@ -77,6 +99,7 @@ define([ realtime: chainpad, readOnly: readOnly }); + evConnected.fire(); }); sframeChan.on('Q_RT_MESSAGE', function (content, cb) { if (isReady) { @@ -92,9 +115,22 @@ define([ setMyID({ myID: myID }); onReady({ realtime: chainpad }); }); + + var whenRealtimeSyncs = function (cb) { + evConnected.reg(function () { + if (chainpad.getAuthDoc() === chainpad.getUserDoc()) { + return void cb(); + } else { + chainpad.onSettle(cb); + } + }); + }; + return Object.freeze({ getMyID: function () { return myID; }, - metadataMgr: metadataMgr + metadataMgr: metadataMgr, + whenRealtimeSyncs: whenRealtimeSyncs, + onInfiniteSpinner: evInfiniteSpinner.reg }); }; return Object.freeze(module.exports); diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index df4259dff..9cceb0948 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -298,6 +298,14 @@ define([ Cryptpad.useTemplate(href, Cryptget, cb); }); + sframeChan.on('EV_GOTO_URL', function (url) { + if (url) { + window.location.href = url; + } else { + window.location.reload(); + } + }); + CpNfOuter.start({ sframeChan: sframeChan, channel: secret.channel, diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index c7ebf1a12..f597cff83 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -12,9 +12,10 @@ define([ '/customize/application_config.js', '/common/cryptpad-common.js', - '/common/common-realtime.js' + '/common/common-realtime.js', + '/common/common-util.js' ], function ($, nThen, Messages, CpNfInner, SFrameChannel, Title, UI, History, File, MetadataMgr, - AppConfig, Cryptpad, CommonRealtime) { + AppConfig, Cryptpad, CommonRealtime, Util) { // Chainpad Netflux Inner var funcs = {}; @@ -22,12 +23,15 @@ define([ funcs.Messages = Messages; + var evRealtimeSynced = Util.mkEvent(true); + funcs.startRealtime = function (options) { if (ctx.cpNfInner) { return ctx.cpNfInner; } options.sframeChan = ctx.sframeChan; options.metadataMgr = ctx.metadataMgr; ctx.cpNfInner = CpNfInner.start(options); ctx.cpNfInner.metadataMgr.onChangeLazy(options.onLocal); + ctx.cpNfInner.whenRealtimeSyncs(function () { evRealtimeSynced.fire(); }); return ctx.cpNfInner; }; @@ -197,6 +201,10 @@ define([ }); }; */ + funcs.gotoURL = function (url) { ctx.sframeChan.event('EV_GOTO_URL', url); }; + + funcs.whenRealtimeSyncs = evRealtimeSynced.reg; + Object.freeze(funcs); return { create: function (cb) { nThen(function (waitFor) { diff --git a/www/common/sframe-protocol.js b/www/common/sframe-protocol.js index 9c79b1495..07c904d66 100644 --- a/www/common/sframe-protocol.js +++ b/www/common/sframe-protocol.js @@ -109,4 +109,7 @@ define({ 'Q_UPLOAD_FILE': true, 'EV_FILE_UPLOAD_STATE': true, 'Q_CANCEL_PENDING_FILE_UPLOAD': true, + + // Make the browser window navigate to a given URL, if no URL is passed then it will reload. + 'EV_GOTO_URL': true, }); diff --git a/www/common/toolbar2.js b/www/common/toolbar2.js index c79a9c233..c0a8f7c6b 100644 --- a/www/common/toolbar2.js +++ b/www/common/toolbar2.js @@ -711,6 +711,7 @@ define([ }, local ? 0 : SPINNER_DISAPPEAR_TIME); }; if (Cryptpad) { + Cryptpad.beginDetectingInfiniteSpinner(config.realtime); Cryptpad.whenRealtimeSyncs(config.realtime, onSynced); return; } @@ -733,6 +734,9 @@ define([ // receive a patch. if (Cryptpad) { typing = 0; + // We're just placing this detector here because it used to be triggered by + // whenRealtimeSyncs() and now it is not because in sframe it is handled differently. + Cryptpad.beginDetectingInfiniteSpinner(config.realtime); Cryptpad.whenRealtimeSyncs(config.realtime, function () { kickSpinner(toolbar, config); }); diff --git a/www/common/toolbar3.js b/www/common/toolbar3.js index 1ffc5c4e8..58e4e1eec 100644 --- a/www/common/toolbar3.js +++ b/www/common/toolbar3.js @@ -673,11 +673,7 @@ define([ $spin.text(Messages.saved); }, local ? 0 : SPINNER_DISAPPEAR_TIME); }; - if (Cryptpad) { - Cryptpad.whenRealtimeSyncs(config.realtime, onSynced); - return; - } - onSynced(); + config.sfCommon.whenRealtimeSyncs(onSynced); }; var ks = function (toolbar, config, local) { return function () { @@ -694,12 +690,10 @@ define([ } // without this, users in read-only mode say 'synchronizing' until they // receive a patch. - if (Cryptpad) { - typing = 0; - Cryptpad.whenRealtimeSyncs(config.realtime, function () { - kickSpinner(toolbar, config); - }); - } + typing = 0; + config.sfCommon.whenRealtimeSyncs(function () { + kickSpinner(toolbar, config); + }); return $spin; }; diff --git a/www/pad/inner.js b/www/pad/inner.js index 5b00fa07d..278a12d7c 100644 --- a/www/pad/inner.js +++ b/www/pad/inner.js @@ -33,7 +33,6 @@ define([ '/bower_components/nthen/index.js', '/common/sframe-common.js', '/api/config', - '/common/common-realtime.js', '/bower_components/file-saver/FileSaver.min.js', '/bower_components/diff-dom/diffDOM.js', @@ -56,8 +55,7 @@ define([ Links, nThen, SFCommon, - ApiConfig, - CommonRealtime) + ApiConfig) { var saveAs = window.saveAs; var Messages = Cryptpad.Messages; @@ -339,8 +337,6 @@ define([ } }; - CommonRealtime.onInfiniteSpinner(function () { setEditable(false); }); - // don't let the user edit until the pad is ready setEditable(false); @@ -717,6 +713,15 @@ define([ cpNfInner = common.startRealtime(realtimeOptions); metadataMgr = cpNfInner.metadataMgr; + cpNfInner.onInfiniteSpinner(function () { + setEditable(false); + Cryptpad.confirm(Messages.realtime_unrecoverableError, function (yes) { + if (!yes) { return; } + common.gotoURL(); + //window.parent.location.reload(); + }); + }); + Cryptpad.onLogout(function () { setEditable(false); }); /* hitting enter makes a new line, but places the cursor inside