diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 8efca25fd..5d90f37f9 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -313,9 +313,30 @@ button:not(.btn).primary:hover{ }; var hasErrored = false; + var isOffline = false; var updateLoadingProgress = function (data) { if (!built || !data) { return; } + // If we receive a "offline" event, show the warning text + if (data.type === "offline") { + try { + isOffline = true; + Messages.offlineError = "OFFLINE MODE NOT AVAILABLE"; // XXX + document.querySelector('#cp-loading-message').setAttribute('style', 'display:block;'); + document.querySelector('#cp-loading-message').innerText = Messages.offlineError; + } catch (e) { console.error(e); } + return; + } + + // If we receive a new event and we were offline, remove + // the offline warning text + if (isOffline) { + try { + isOffline = false; + document.querySelector('#cp-loading-message').setAttribute('style', 'display:none;'); + } catch (e) { console.error(e); } + } + // Make sure progress doesn't go backward var c = types.indexOf(data.type); if (c < current) { return console.debug(data); } diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index d047c4d4b..4bd5171bf 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -492,23 +492,38 @@ define([ }); }; + // This function is used when we want to open a pad. We first need + // to check if it exists. With the cached drive, we need to wait for + // the network to be available before we can continue. common.isNewChannel = function (href, password, _cb) { var cb = Util.once(Util.mkAsync(_cb)); var channel = Hash.hrefToHexChannelId(href, password); var error; + var inCache = false; Nthen(function (waitFor) { - // If it's not in the cache or it's not a blob, try to get the value from the server - postMessage('IS_NEW_CHANNEL', {channel: channel}, waitFor(function (obj) { - if (obj && obj.error) { error = obj.error; return; } - if (!obj) { error = "INVALID_RESPONSE"; return; } + Cache.getChannelCache(channel, waitFor(function(err, data) { + if (err || !data) { return; } waitFor.abort(); - cb(undefined, obj.isNew); + cb(undefined, false); })); }).nThen(function () { - Cache.getChannelCache(channel, function(err, data) { - if (err || !data) { return void cb(error); } - cb(null, false); - }); + // If it's not in the cache try to get the value from the server + var isNew = function () { + error = undefined; + postMessage('IS_NEW_CHANNEL', {channel: channel}, function (obj) { + if (obj && obj.error) { error = obj.error; } + if (!obj) { error = "INVALID_RESPONSE"; } + + if (error === "ANON_RPC_NOT_READY") { + // Try again in 1s + return void setTimeout(isNew, 100); + } else if (error) { + return void cb(error); + } + cb(undefined, obj.isNew); + }, {timeout: -1}); + }; + isNew(); }); }; diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 5d26534fb..41a1d00aa 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -35,6 +35,7 @@ define([ Crypto, ChainPad, CpNetflux, Listmap, nThen, Saferphore) { var onReadyEvt = Util.mkEvent(true); + var onCacheReadyEvt = Util.mkEvent(true); // Default settings for new users var NEW_USER_SETTINGS = { @@ -2651,7 +2652,8 @@ define([ Feedback.init(returned.feedback); // "cb" may have already been called by onCacheReady - if (typeof(cb) === 'function') { cb(returned); } + store.returned = returned; + if (typeof(cb) === 'function') { cb(); } store.offline = false; sendDriveEvent('NETWORK_RECONNECT'); // Tell inner that we're now online @@ -2762,6 +2764,23 @@ define([ store.realtime = info.realtime; store.networkPromise = info.networkPromise; store.cacheReturned = returned; + + // Check if we can connect + var to = setTimeout(function () { + console.error('TO'); + store.networkTimeout = true; + broadcast([], "LOADING_DRIVE", { + type: "offline" + }); + }, 5000); + + store.networkPromise.then(function () { + clearTimeout(to); + }, function (err) { + console.error(err); + clearTimeout(to); + }); + if (!data.cache) { return; } // Make sure we have a valid user object before emitting cacheready @@ -2769,8 +2788,10 @@ define([ onCacheReady(clientId, function () { if (typeof(cb) === "function") { cb(returned); } + onCacheReadyEvt.fire(); }); }).on('ready', function (info) { + delete store.networkTimeout; if (store.ready) { return; } // the store is already ready, it is a reconnection store.driveMetadata = info.metadata; if (!rt.proxy.drive || typeof(rt.proxy.drive) !== 'object') { rt.proxy.drive = {}; } @@ -2859,15 +2880,26 @@ define([ Store.init = function (clientId, data, _callback) { var callback = Util.once(_callback); - if (!store.returned && data.cache && store.cacheReturned) { - return void onCacheReady(clientId, function () { + + // If this is not the first tab and we're offline, callback only if the app + // supports offline mode + if (initialized && !store.returned && data.cache) { + return void onCacheReadyEvt.reg(function () { callback({ state: 'ALREADY_INIT', returned: store.cacheReturned }); }); } + + // If this is not the first tab (initialized is true), it means either we don't + // support offline or we're already online if (initialized) { + if (store.networkTimeout) { + postMessage(clientId, "LOADING_DRIVE", { + type: "offline" + }); + } return void whenReady(function () { callback({ state: 'ALREADY_INIT', @@ -2890,8 +2922,6 @@ define([ } if (ret && ret.error) { initialized = false; - } else { - store.returned = ret; } callback(ret); diff --git a/www/common/outer/sharedworker.js b/www/common/outer/sharedworker.js index a89376407..825deb383 100644 --- a/www/common/outer/sharedworker.js +++ b/www/common/outer/sharedworker.js @@ -73,6 +73,7 @@ var init = function (client, cb) { }); chan.on('CONNECT', function (cfg, cb) { debug('SharedW connecting to store...'); + /* if (self.store) { debug('Store already exists!'); if (cfg.driveEvents) { @@ -80,31 +81,35 @@ var init = function (client, cb) { } return void cb(self.store); } + */ - debug('Loading new async store'); - // One-time initialization (init async-store) - cfg.query = function (cId, cmd, data, cb) { - cb = cb || function () {}; - self.tabs[cId].chan.query(cmd, data, function (err, data2) { - if (err) { return void cb({error: err}); } - cb(data2); - }); - }; - cfg.broadcast = function (excludes, cmd, data, cb) { - cb = cb || function () {}; - Object.keys(self.tabs).forEach(function (cId) { - if (excludes.indexOf(cId) !== -1) { return; } + if (!self.store) { + debug('Loading new async store'); + // One-time initialization (init async-store) + cfg.query = function (cId, cmd, data, cb) { + cb = cb || function () {}; self.tabs[cId].chan.query(cmd, data, function (err, data2) { if (err) { return void cb({error: err}); } cb(data2); }); - }); - }; + }; + cfg.broadcast = function (excludes, cmd, data, cb) { + cb = cb || function () {}; + Object.keys(self.tabs).forEach(function (cId) { + if (excludes.indexOf(cId) !== -1) { return; } + self.tabs[cId].chan.query(cmd, data, function (err, data2) { + if (err) { return void cb({error: err}); } + cb(data2); + }); + }); + }; + } Rpc.queries['CONNECT'](clientId, cfg, function (data) { if (cfg.driveEvents) { Rpc._subscribeToDrive(clientId); } if (data && data.state === "ALREADY_INIT") { + debug('Store already exists!'); self.store = data.returned; return void cb(data.returned); } diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index e2bb8077c..1d5597ab2 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -498,11 +498,6 @@ define([ // We've received a link without /p/ and it doesn't work without a password: abort return void todo(); } - if (e === "ANON_RPC_NOT_READY") { - // We're currently offline and the pad is not in our cache - w.abort(); - return void sframeChan.event('EV_OFFLINE'); - } // Wrong password or deleted file? askPassword(true, passwordCfg); })); diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index f88f98beb..8a3328d8a 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -772,10 +772,6 @@ define([ UI.errorLoadingScreen(Messages.restrictedError); }); - ctx.sframeChan.on("EV_OFFLINE", function () { - UI.errorLoadingScreen("OFFLINE AND NO CACHE"); // XXX - }); - ctx.sframeChan.on("EV_PAD_PASSWORD_ERROR", function () { UI.errorLoadingScreen(Messages.password_error_seed); });