diff --git a/lib/hk-util.js b/lib/hk-util.js index 3f0aed360..3a904367a 100644 --- a/lib/hk-util.js +++ b/lib/hk-util.js @@ -424,7 +424,7 @@ const storeMessage = function (Env, channel, msg, isCp, optionalMessageHash) { * it has a side-effect of filling the index cache if it's empty 1. if you provided a lastKnownHash and that message does not exist in the history: * either the client has made a mistake or the history they knew about no longer exists - * call back with EINVAL + * call back with EUNKNOWN 2. if you did not provide a lastKnownHash * and there are fewer than two checkpoints: * return 0 (read from the start of the file) @@ -460,7 +460,7 @@ const getHistoryOffset = (Env, channelName, lastKnownHash, cb) => { // QUESTION: does this mean mailboxes are causing the server to store too much stuff in memory? if (lastKnownHash && typeof(lkh) !== "number") { waitFor.abort(); - return void cb(new Error('EINVAL')); + return void cb(new Error('EUNKNOWN')); } // Since last 2 checkpoints diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 497babd22..13bdf26a1 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1499,6 +1499,15 @@ define([ return; } + var onError = function (err) { + console.warn(err); // XXX DEBUG + + channel.bcast("PAD_ERROR", err); + + // If this is a DELETED, EXPIRED or RESTRICTED pad, leave the channel + if (["EDELETED", "EEXPIRED", "ERESTRICTED"].indexOf(err.type) === -1) { return; } + Store.leavePad(null, data, function () {}); + }; var conf = { onReady: function (pad) { var padData = pad.metadata || {}; @@ -1522,18 +1531,8 @@ define([ onLeave: function (m) { channel.bcast("PAD_LEAVE", m); }, - onError: function (err) { - console.error(err); // XXX DEBUG - - channel.bcast("PAD_ERROR", err); - Store.leavePad(null, data, function () {}); - }, - onChannelError: function (err) { - console.warn(err); // XXX DEBUG - - channel.bcast("PAD_ERROR", err); - Store.leavePad(null, data, function () {}); - }, + onError: onError, + onChannelError: onError, onRejected: function (allowed, _cb) { var cb = Util.once(Util.mkAsync(_cb)); diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index f4567ee70..aa1d6f10f 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -325,6 +325,11 @@ define([ UI.updateLoadingProgress({ state: -1 }, false); + if (toolbar) { + // Check if we have a new chainpad instance + toolbar.resetChainpad(cpNfInner.chainpad); + } + var newPad = false; if (newContentStr === '') { newPad = true; } @@ -364,6 +369,9 @@ define([ }).nThen(function () { stateChange(STATE.READY); firstConnection = false; + + oldContent = undefined; + if (!readOnly) { onLocal(); } evOnReady.fire(newPad); diff --git a/www/common/sframe-chainpad-netflux-inner.js b/www/common/sframe-chainpad-netflux-inner.js index 65feb67da..a55463bb7 100644 --- a/www/common/sframe-chainpad-netflux-inner.js +++ b/www/common/sframe-chainpad-netflux-inner.js @@ -49,24 +49,29 @@ define([ config = undefined; var evPatchSent = Util.mkEvent(); + var chainpad; - var chainpad = ChainPad.create({ - userName: userName, - initialState: initialState, - patchTransformer: patchTransformer, - validateContent: validateContent, - avgSyncMilliseconds: avgSyncMilliseconds, - logLevel: logLevel - }); - chainpad.onMessage(function(message, cb) { - sframeChan.query('Q_RT_MESSAGE', message, function (err) { - if (!err) { evPatchSent.fire(); } - cb(err); + var makeChainPad = function () { + var _chainpad = ChainPad.create({ + userName: userName, + initialState: initialState, + patchTransformer: patchTransformer, + validateContent: validateContent, + avgSyncMilliseconds: avgSyncMilliseconds, + logLevel: logLevel }); - }); - chainpad.onPatch(function () { - onRemote({ realtime: chainpad }); - }); + _chainpad.onMessage(function(message, cb) { + sframeChan.query('Q_RT_MESSAGE', message, function (err) { + if (!err) { evPatchSent.fire(); } + cb(err); + }); + }); + _chainpad.onPatch(function () { + onRemote({ realtime: chainpad }); + }); + return _chainpad; + }; + chainpad = makeChainPad(); var myID; var isReady = false; @@ -96,6 +101,11 @@ define([ sframeChan.on('EV_RT_ERROR', function (err) { isReady = false; chainpad.abort(); + if (err.type === 'EUNKNOWN') { // XXX + // Recoverable error: make a new chainpad + chainpad = makeChainPad(); + return; + } onError(err); }); sframeChan.on('EV_RT_CONNECT', function (content) { @@ -149,15 +159,18 @@ define([ }); }; - return Object.freeze({ + var cpNfInner = { getMyID: function () { return myID; }, metadataMgr: metadataMgr, whenRealtimeSyncs: whenRealtimeSyncs, onInfiniteSpinner: evInfiniteSpinner.reg, onPatchSent: evPatchSent.reg, offPatchSent: evPatchSent.unreg, - chainpad: chainpad, + }; + cpNfInner.__defineGetter__("chainpad", function () { + return chainpad; }); + return Object.freeze(cpNfInner); }; return Object.freeze(module.exports); }); diff --git a/www/common/toolbar3.js b/www/common/toolbar3.js index ef42fcaea..2473a0517 100644 --- a/www/common/toolbar3.js +++ b/www/common/toolbar3.js @@ -1331,6 +1331,15 @@ MessengerUI, Messages) { showColors = true; }; + // If we had to create a new chainpad instance, reset the one used in the toolbar + toolbar.resetChainpad = function (chainpad) { + if (config.realtime !== chainpad) { + config.realtime = chainpad; + config.realtime.onPatch(ks(toolbar, config)); + config.realtime.onMessage(ks(toolbar, config, true)); + } + }; + // On log out, remove permanently the realtime elements of the toolbar Common.onLogout(function () { failed(); diff --git a/www/poll/inner.js b/www/poll/inner.js index 6c8e77482..38ed1b79c 100644 --- a/www/poll/inner.js +++ b/www/poll/inner.js @@ -794,6 +794,11 @@ define([ var userDoc = JSON.stringify(proxy); if (userDoc === "" || userDoc === "{}") { isNew = true; } + if (APP.toolbar && APP.rt.cpCnInner) { + // Check if we have a new chainpad instance + APP.toolbar.resetChainpad(APP.rt.cpCnInner.chainpad); + } + if (!isNew) { if (proxy.info) { // Migration