From 980d3c7fff2a15f136901e62f0b4f3e82dec3df8 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 4 Nov 2020 17:59:08 +0100 Subject: [PATCH 1/8] Load the drive and its shared folders without any network --- customize.dist/loading.js | 9 ++-- www/common/drive-ui.js | 8 +++- www/common/outer/async-store.js | 72 ++++++++++++++++++++++++++++---- www/common/outer/sharedfolder.js | 30 ++++++++++--- www/common/proxy-manager.js | 28 +++++++++---- 5 files changed, 121 insertions(+), 26 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 733b3fff0..85a4a0b7e 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -317,9 +317,12 @@ button.primary:hover{ var c = types.indexOf(data.type); if (c < current) { return console.error(data); } try { - document.querySelector('.cp-loading-spinner-container').style.display = 'none'; - document.querySelector('.cp-loading-progress-list').innerHTML = makeList(data); - document.querySelector('.cp-loading-progress-container').innerHTML = makeBar(data); + var a = document.querySelector('.cp-loading-spinner-container'); + if (a) { a.style.display = 'none'; } + var b = document.querySelector('.cp-loading-progress-list'); + if (b) { b.innerHTML = makeList(data); } + var c = document.querySelector('.cp-loading-progress-container'); + if (c) { c.innerHTML = makeBar(data); } } catch (e) { console.error(e); } }; window.CryptPad_updateLoadingProgress = updateLoadingProgress; diff --git a/www/common/drive-ui.js b/www/common/drive-ui.js index 4886b213e..f97855105 100644 --- a/www/common/drive-ui.js +++ b/www/common/drive-ui.js @@ -42,7 +42,7 @@ define([ var APP = window.APP = { editable: false, - online: true, + online: false, mobile: function () { if (window.matchMedia) { return !window.matchMedia('(any-pointer:fine)').matches; } else { return $('body').width() <= 600; } @@ -4361,8 +4361,12 @@ define([ var anonDrive = manager.isPathIn(currentPath, [FILES_DATA]) && !APP.loggedIn; if (manager.isFolder(el) && !manager.isSharedFolder(el) && !anonDrive) { // Folder + // disconnected + if (!APP.editable) { + return void UI.warn(Messages.error); // XXX + } // if folder is inside SF - if (manager.isInSharedFolder(paths[0].path)) { + else if (manager.isInSharedFolder(paths[0].path)) { return void UI.alert(Messages.convertFolderToSF_SFParent); } // if folder already contains SF diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 7066d3921..89ed1dbbf 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -89,7 +89,7 @@ define([ Realtime.whenRealtimeSyncs(s.sharedFolders[k].realtime, waitFor()); } } - }).nThen(function () { cb(); }); + }).nThen(function () { console.log('done');cb(); }); }; Store.get = function (clientId, data, cb) { @@ -2162,7 +2162,7 @@ define([ if (!s) { return void cb({ error: 'ENOTFOUND' }); } SF.load({ isNew: isNew, - network: store.network, + network: store.network || store.networkPromise, store: s, isNewChannel: Store.isNewChannel }, id, data, cb); @@ -2465,8 +2465,45 @@ define([ }); }; + var onCacheReady = function (clientId, cb) { + var proxy = store.proxy; + if (!proxy.settings) { proxy.settings = NEW_USER_SETTINGS; } + if (!proxy.friends_pending) { proxy.friends_pending = {}; } + var unpin = function (data, cb) { + if (!store.loggedIn) { return void cb(); } + Store.unpinPads(null, data, cb); + }; + var pin = function (data, cb) { + if (!store.loggedIn) { return void cb(); } + Store.pinPads(null, data, cb); + }; + var manager = store.manager = ProxyManager.create(proxy.drive, { + onSync: function (cb) { onSync(null, cb); }, + edPublic: proxy.edPublic, + pin: pin, + unpin: unpin, + loadSharedFolder: loadSharedFolder, + settings: proxy.settings, + removeOwnedChannel: function (channel, cb) { Store.removeOwnedChannel('', channel, cb); }, + Store: Store + }, { + outer: true, + edPublic: store.proxy.edPublic, + loggedIn: store.loggedIn, + log: function (msg) { + // broadcast to all drive apps + sendDriveEvent("DRIVE_LOG", msg); + }, + rt: store.realtime + }); + var userObject = store.userObject = manager.user.userObject; + addSharedFolderHandler(); + userObject.migrate(cb); + }; var onReady = function (clientId, returned, cb) { + console.error('READY'); var proxy = store.proxy; + /* var unpin = function (data, cb) { if (!store.loggedIn) { return void cb(); } Store.unpinPads(null, data, cb); @@ -2498,9 +2535,16 @@ define([ }); var userObject = store.userObject = manager.user.userObject; addSharedFolderHandler(); + */ + store.ready = true; + var manager = store.manager; + var userObject = store.userObject; nThen(function (waitFor) { - userObject.migrate(waitFor()); + if (manager) { return; } + onCacheReady(clientId, waitFor); + manager = store.manager; + userObject = store.userObject; }).nThen(function (waitFor) { initAnonRpc(null, null, waitFor()); initRpc(null, null, waitFor()); @@ -2533,12 +2577,12 @@ define([ loadUniversal(Messenger, 'messenger', waitFor); store.messenger = store.modules['messenger']; loadUniversal(Profile, 'profile', waitFor); - loadUniversal(Team, 'team', waitFor, clientId); + loadUniversal(Team, 'team', waitFor, clientId); // XXX load teams offline? loadUniversal(History, 'history', waitFor); cleanFriendRequests(); }).nThen(function () { - var requestLogin = function () { - broadcast([], "REQUEST_LOGIN"); + var requestLogin = function () { + broadcast([], "REQUEST_LOGIN"); }; if (store.loggedIn) { @@ -2571,7 +2615,10 @@ define([ returned.feedback = Util.find(proxy, ['settings', 'general', 'allowUserFeedback']); Feedback.init(returned.feedback); - if (typeof(cb) === 'function') { cb(returned); } + // XXX send feedback and logintoken to outer... + //if (typeof(cb) === 'function') { cb(returned); } + store.offline = false; + sendDriveEvent('NETWORK_RECONNECT'); if (typeof(proxy.uid) !== 'string' || proxy.uid.length !== 32) { // even anonymous users should have a persistent, unique-ish id @@ -2670,8 +2717,17 @@ define([ if (!data.userHash) { returned.anonHash = Hash.getEditHashFromKeys(secret); } + if (typeof(cb) === "function") { cb(returned); } + }).on('cacheready', function (info) { + store.offline = true; + store.realtime = info.realtime; + store.networkPromise = info.networkPromise; + // XXX make sure we have a valid drive available + onCacheReady(clientId, function () { + if (typeof(cb) === "function") { cb(returned); } + }); }).on('ready', function (info) { - if (store.userObject) { return; } // the store is already ready, it is a reconnection + 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 = {}; } var drive = rt.proxy.drive; diff --git a/www/common/outer/sharedfolder.js b/www/common/outer/sharedfolder.js index 3c1c8632d..830bef05b 100644 --- a/www/common/outer/sharedfolder.js +++ b/www/common/outer/sharedfolder.js @@ -128,11 +128,11 @@ define([ var uo = store.manager.addProxy(id, sf.rt, leave, secondaryKey); // NOTE: Shared folder migration, disable for now SF.checkMigration(secondaryKey, sf.rt.proxy, uo, function () { - cb(sf.rt, sf.metadata); + cb(sf.rt); }); */ store.manager.addProxy(id, sf.rt, leave, secondaryKey); - cb(sf.rt, sf.metadata); + cb(sf.rt); }); sf.teams.push({ cb: cb, @@ -182,6 +182,26 @@ define([ } }; var rt = sf.rt = Listmap.create(listmapConfig); + rt.proxy.on('cacheready', function (info) { + if (isNew && !Object.keys(rt.proxy).length) { + // New Shared folder: no migration required + rt.proxy.version = 2; + } + if (!sf.teams) { + return; + } + sf.teams.forEach(function (obj) { + var leave = function () { SF.leave(secret.channel, obj.store.id); }; + + // We can safely call addProxy and obj.cb here because + // 1. addProxy won't re-add the same folder twice on 'ready' + // 2. obj.cb is using Util.once + rt.cache = true; + obj.store.manager.addProxy(obj.id, rt, leave, obj.secondaryKey); + obj.cb(sf.rt); + }); + sf.ready = true; + }); rt.proxy.on('ready', function (info) { if (isNew && !Object.keys(rt.proxy).length) { // New Shared folder: no migration required @@ -196,13 +216,13 @@ define([ var uo = obj.store.manager.addProxy(obj.id, rt, leave, obj.secondaryKey); // NOTE: Shared folder migration, disable for now SF.checkMigration(secondaryKey, rt.proxy, uo, function () { - obj.cb(sf.rt, info.metadata); + obj.cb(sf.rt); }); */ + rt.cache = false; obj.store.manager.addProxy(obj.id, rt, leave, obj.secondaryKey); - obj.cb(sf.rt, info.metadata); + obj.cb(sf.rt); }); - sf.metadata = info.metadata; sf.ready = true; }); rt.proxy.on('error', function (info) { diff --git a/www/common/proxy-manager.js b/www/common/proxy-manager.js index 74bfcd890..17b4fec55 100644 --- a/www/common/proxy-manager.js +++ b/www/common/proxy-manager.js @@ -17,6 +17,15 @@ define([ // Add a shared folder to the list var addProxy = function (Env, id, lm, leave, editKey) { + if (Env.folders[id]) { + // Shared folder already added to the proxy-manager, probably + // a cached version + if (Env.folders[id].offline && !lm.cache) { + Env.folders[id].offline = false; + Env.Store.refreshDriveUI(); + } + return; + } var cfg = getConfig(Env); cfg.sharedFolder = true; cfg.id = id; @@ -38,14 +47,15 @@ define([ Env.folders[id] = { proxy: lm.proxy, userObject: userObject, - leave: leave + leave: leave, + offline: Boolean(lm.cache) }; if (proxy.on) { proxy.on('disconnect', function () { Env.folders[id].offline = true; }); proxy.on('reconnect', function () { - Env.folders[id].online = true; + Env.folders[id].offline = false; }); } return userObject; @@ -537,7 +547,7 @@ define([ Env.user.userObject.add(id, resolved.path); // 2b. load the proxy - Env.loadSharedFolder(id, folderData, waitFor(function (rt, metadata) { + Env.loadSharedFolder(id, folderData, waitFor(function (rt) { if (!rt) { waitFor.abort(); return void cb({ error: 'EDELETED' }); @@ -546,11 +556,13 @@ define([ if (!rt.proxy.metadata) { // Creating a new shared folder rt.proxy.metadata = { title: data.name || Messages.fm_newFolder }; } - // If we're importing a folder, check its serverside metadata - if (data.folderData && metadata) { - var fData = Env.user.proxy[UserObject.SHARED_FOLDERS][id]; - if (metadata.owners) { fData.owners = metadata.owners; } - if (metadata.expire) { fData.expire = +metadata.expire; } + if (data.folderData) { + // If we're importing a folder, check its serverside metadata + Env.Store.getPadMetadata(null, { channel: folderData.channel }, function (md) { + var fData = Env.user.proxy[UserObject.SHARED_FOLDERS][id]; + if (md.owners) { fData.owners = md.owners; } + if (md.expire) { fData.expire = +md.expire; } + }); } }), !Boolean(data.folderData)); }).nThen(function () { From bb13165e865f665d114e86b1f7ec481e4fbc7055 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 4 Nov 2020 18:20:57 +0100 Subject: [PATCH 2/8] lint compliance --- customize.dist/loading.js | 12 ++++++------ www/common/outer/async-store.js | 4 ++-- www/common/outer/sharedfolder.js | 4 ++-- www/common/sframe-app-framework.js | 3 +-- www/common/sframe-chainpad-netflux-inner.js | 4 ++-- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 85a4a0b7e..344c61942 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -317,12 +317,12 @@ button.primary:hover{ var c = types.indexOf(data.type); if (c < current) { return console.error(data); } try { - var a = document.querySelector('.cp-loading-spinner-container'); - if (a) { a.style.display = 'none'; } - var b = document.querySelector('.cp-loading-progress-list'); - if (b) { b.innerHTML = makeList(data); } - var c = document.querySelector('.cp-loading-progress-container'); - if (c) { c.innerHTML = makeBar(data); } + var el1 = document.querySelector('.cp-loading-spinner-container'); + if (el1) { el1.style.display = 'none'; } + var el2 = document.querySelector('.cp-loading-progress-list'); + if (el2) { el2.innerHTML = makeList(data); } + var el3 = document.querySelector('.cp-loading-progress-container'); + if (el3) { el3.innerHTML = makeBar(data); } } catch (e) { console.error(e); } }; window.CryptPad_updateLoadingProgress = updateLoadingProgress; diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 89ed1dbbf..a2101a7ff 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1598,7 +1598,7 @@ define([ onCacheStart: function () { postMessage(clientId, "PAD_CACHE"); }, - onCacheReady: function (info) { + onCacheReady: function () { postMessage(clientId, "PAD_CACHE_READY"); }, onReady: function (pad) { @@ -2500,7 +2500,7 @@ define([ addSharedFolderHandler(); userObject.migrate(cb); }; - var onReady = function (clientId, returned, cb) { + var onReady = function (clientId, returned/*, cb*/) { console.error('READY'); var proxy = store.proxy; /* diff --git a/www/common/outer/sharedfolder.js b/www/common/outer/sharedfolder.js index 830bef05b..3b0407bf3 100644 --- a/www/common/outer/sharedfolder.js +++ b/www/common/outer/sharedfolder.js @@ -182,7 +182,7 @@ define([ } }; var rt = sf.rt = Listmap.create(listmapConfig); - rt.proxy.on('cacheready', function (info) { + rt.proxy.on('cacheready', function () { if (isNew && !Object.keys(rt.proxy).length) { // New Shared folder: no migration required rt.proxy.version = 2; @@ -202,7 +202,7 @@ define([ }); sf.ready = true; }); - rt.proxy.on('ready', function (info) { + rt.proxy.on('ready', function () { if (isNew && !Object.keys(rt.proxy).length) { // New Shared folder: no migration required rt.proxy.version = 2; diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index 81ab18d45..24f07e48e 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -486,7 +486,6 @@ console.log(newContentStr); var newContent = JSON.parse(newContentStr); var metadata = extractMetadata(newContent); -console.log('OKOK'); // Make sure we're using the correct app for this cache if (metadata && typeof(metadata.type) !== 'undefined' && metadata.type !== type) { @@ -497,7 +496,7 @@ console.log('OKOK'); cpNfInner.metadataMgr.updateMetadata(metadata); newContent = normalize(newContent); if (!unsyncMode) { - contentUpdate(newContent, function () { return function () {}}); + contentUpdate(newContent, function () { return function () {}; }); } UI.removeLoadingScreen(emitResize); diff --git a/www/common/sframe-chainpad-netflux-inner.js b/www/common/sframe-chainpad-netflux-inner.js index 57689b7ea..08625dc77 100644 --- a/www/common/sframe-chainpad-netflux-inner.js +++ b/www/common/sframe-chainpad-netflux-inner.js @@ -94,10 +94,10 @@ define([ evInfiniteSpinner.fire(); }, 2000); - sframeChan.on('EV_RT_CACHE', function (isPermanent) { + sframeChan.on('EV_RT_CACHE', function () { // XXX }); - sframeChan.on('EV_RT_CACHE_READY', function (isPermanent) { + sframeChan.on('EV_RT_CACHE_READY', function () { // XXX onCacheReady({realtime: chainpad}); console.error('PEWPEWPEW'); From 65628a51692d86269696de655b275e3b33e19a5a Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 5 Nov 2020 14:02:03 +0100 Subject: [PATCH 3/8] Only use offline cache in the drive app for now --- www/common/cryptpad-common.js | 1 + www/common/outer/async-store.js | 81 ++++++++++++------------------- www/common/sframe-common-outer.js | 1 + www/drive/inner.js | 2 + www/drive/main.js | 1 + 5 files changed, 37 insertions(+), 49 deletions(-) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 82ea2991f..9e65dba93 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -2108,6 +2108,7 @@ define([ anonHash: LocalStore.getFSHash(), localToken: tryParsing(localStorage.getItem(Constants.tokenKey)), // TODO move this to LocalStore ? language: common.getLanguage(), + cache: rdyCfg.cache, driveEvents: true //rdyCfg.driveEvents // Boolean }; diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index a2101a7ff..ec9c36d14 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -34,6 +34,8 @@ define([ NetConfig, AppConfig, Crypto, ChainPad, CpNetflux, Listmap, nThen, Saferphore) { + var onReadyEvt = Util.mkEvent(true); + // Default settings for new users var NEW_USER_SETTINGS = { drive: { @@ -595,6 +597,7 @@ define([ pendingFriends: store.proxy.friends_pending || {}, supportPrivateKey: Util.find(store.proxy, ['mailboxes', 'supportadmin', 'keys', 'curvePrivate']), accountName: store.proxy.login_name || '', + offline: store.offline, teams: teams, plan: account.plan } @@ -2178,16 +2181,18 @@ define([ }); }; Store.addSharedFolder = function (clientId, data, cb) { - var s = getStore(data.teamId); - s.manager.addSharedFolder(data, function (id) { - if (id && typeof(id) === "object" && id.error) { - return void cb(id); - } - var send = data.teamId ? s.sendEvent : sendDriveEvent; - send('DRIVE_CHANGE', { - path: ['drive', UserObject.FILES_DATA] - }, clientId); - cb(id); + onReadyEvt.reg(function () { + var s = getStore(data.teamId); + s.manager.addSharedFolder(data, function (id) { + if (id && typeof(id) === "object" && id.error) { + return void cb(id); + } + var send = data.teamId ? s.sendEvent : sendDriveEvent; + send('DRIVE_CHANGE', { + path: ['drive', UserObject.FILES_DATA] + }, clientId); + cb(id); + }); }); }; Store.updateSharedFolderPassword = function (clientId, data, cb) { @@ -2500,49 +2505,20 @@ define([ addSharedFolderHandler(); userObject.migrate(cb); }; - var onReady = function (clientId, returned/*, cb*/) { + + // onReady: called when the drive is synced (not using the cache anymore) + // "cb" is wrapped in Util.once() and may have already been called + // if we have a local cache + var onReady = function (clientId, returned, cb) { console.error('READY'); - var proxy = store.proxy; - /* - var unpin = function (data, cb) { - if (!store.loggedIn) { return void cb(); } - Store.unpinPads(null, data, cb); - }; - var pin = function (data, cb) { - if (!store.loggedIn) { return void cb(); } - Store.pinPads(null, data, cb); - }; - if (!proxy.settings) { proxy.settings = NEW_USER_SETTINGS; } - if (!proxy.friends_pending) { proxy.friends_pending = {}; } - var manager = store.manager = ProxyManager.create(proxy.drive, { - onSync: function (cb) { onSync(null, cb); }, - edPublic: proxy.edPublic, - pin: pin, - unpin: unpin, - loadSharedFolder: loadSharedFolder, - settings: proxy.settings, - removeOwnedChannel: function (channel, cb) { Store.removeOwnedChannel('', channel, cb); }, - Store: Store - }, { - outer: true, - edPublic: store.proxy.edPublic, - loggedIn: store.loggedIn, - log: function (msg) { - // broadcast to all drive apps - sendDriveEvent("DRIVE_LOG", msg); - }, - rt: store.realtime - }); - var userObject = store.userObject = manager.user.userObject; - addSharedFolderHandler(); - */ store.ready = true; + var proxy = store.proxy; var manager = store.manager; var userObject = store.userObject; nThen(function (waitFor) { if (manager) { return; } - onCacheReady(clientId, waitFor); + onCacheReady(clientId, waitFor()); manager = store.manager; userObject = store.userObject; }).nThen(function (waitFor) { @@ -2616,9 +2592,12 @@ define([ Feedback.init(returned.feedback); // XXX send feedback and logintoken to outer... - //if (typeof(cb) === 'function') { cb(returned); } - store.offline = false; + // "cb" may have already been called by onCacheReady + if (typeof(cb) === 'function') { cb(returned); } sendDriveEvent('NETWORK_RECONNECT'); + store.offline = false; +// XXX broadcast READY event with the missing data +// XXX we can improve feedback to queue the queries and send them when coming back online if (typeof(proxy.uid) !== 'string' || proxy.uid.length !== 32) { // even anonymous users should have a persistent, unique-ish id @@ -2673,6 +2652,8 @@ define([ broadcast([], "UPDATE_TOKEN", { token: proxy[Constants.tokenKey] }); }); + onReadyEvt.fire(); + loadMailbox(); }); }; @@ -2717,8 +2698,8 @@ define([ if (!data.userHash) { returned.anonHash = Hash.getEditHashFromKeys(secret); } - if (typeof(cb) === "function") { cb(returned); } }).on('cacheready', function (info) { + if (!data.cache) { return; } store.offline = true; store.realtime = info.realtime; store.networkPromise = info.networkPromise; @@ -2753,10 +2734,12 @@ define([ rt.proxy.on('disconnect', function () { store.offline = true; sendDriveEvent('NETWORK_DISCONNECT'); + broadcast([], "UPDATE_METADATA"); }); rt.proxy.on('reconnect', function () { store.offline = false; sendDriveEvent('NETWORK_RECONNECT'); + broadcast([], "UPDATE_METADATA"); }); // Ping clients regularly to make sure one tab was not closed without sending a removeClient() diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index b7ce64d4d..b71fbc249 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -151,6 +151,7 @@ define([ Cryptpad.ready(waitFor(), { driveEvents: cfg.driveEvents, + cache: Boolean(cfg.cache), currentPad: currentPad }); diff --git a/www/drive/inner.js b/www/drive/inner.js index 9aa080dc3..364d4a9d9 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -278,6 +278,7 @@ define([ if (!proxy.drive || typeof(proxy.drive) !== 'object') { throw new Error("Corrupted drive"); } + APP.online = !privateData.offline; var drive = DriveUI.create(common, { $limit: usageBar && usageBar.$container, proxy: proxy, @@ -309,6 +310,7 @@ define([ onDisconnect(); }); sframeChan.on('EV_NETWORK_RECONNECT', function () { + console.log('here'); onReconnect(); }); common.onLogout(function () { setEditable(false); }); diff --git a/www/drive/main.js b/www/drive/main.js index 5eb9dbc67..418338d58 100644 --- a/www/drive/main.js +++ b/www/drive/main.js @@ -129,6 +129,7 @@ define([ hash: hash, href: href, afterSecrets: afterSecrets, + cache: true, noHash: true, noRealtime: true, driveEvents: true, From 989020a436d763f4c2e95babd36867beebeb3b6a Mon Sep 17 00:00:00 2001 From: yflory Date: Fri, 6 Nov 2020 17:13:41 +0100 Subject: [PATCH 4/8] Open pads in offline mode --- www/common/cryptpad-common.js | 57 ++++++++++++++++++++++++----- www/common/inner/common-mediatag.js | 2 +- www/common/messenger-ui.js | 1 + www/common/outer/async-store.js | 12 +++--- www/common/sframe-app-outer.js | 1 + www/common/sframe-common-outer.js | 4 ++ www/common/sframe-common.js | 30 +++++++++++---- www/common/toolbar.js | 5 ++- 8 files changed, 88 insertions(+), 24 deletions(-) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 2ac4101f7..2c8c64099 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -11,11 +11,13 @@ define([ '/common/outer/local-store.js', '/common/outer/worker-channel.js', '/common/outer/login-block.js', + '/common/outer/cache-store.js', '/customize/application_config.js', '/bower_components/nthen/index.js', ], function (Config, Messages, Util, Hash, Messaging, Constants, Feedback, Visible, UserObject, LocalStore, Channel, Block, + Cache, AppConfig, Nthen) { /* This file exposes functionality which is specific to Cryptpad, but not to @@ -437,10 +439,35 @@ define([ }); }; - common.getFileSize = function (href, password, cb) { - postMessage("GET_FILE_SIZE", {href: href, password: password}, function (obj) { - if (obj && obj.error) { return void cb(obj.error); } - cb(undefined, obj.size); + common.getFileSize = function (href, password, _cb) { + var cb = Util.once(Util.mkAsync(_cb)); + var channel = Hash.hrefToHexChannelId(href, password); + var error; + Nthen(function (waitFor) { + // Blobs can't change, if it's in the cache, use it + Cache.getBlobCache(channel, waitFor(function(err, blob) { + if (err) { return; } + waitFor.abort(); + cb(null, blob.length); + })); + + }).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("GET_FILE_SIZE", {channel:channel}, waitFor(function (obj) { + if (obj && obj.error) { + // If disconnected, try to get the value from the channel cache (next nThen) + error = obj.error; + return; + } + waitFor.abort(); + cb(undefined, obj.size); + })); + }).nThen(function () { + Cache.getChannelCache(channel, function(err, data) { + if (err) { return void cb(error); } + var size = data && Array.isArray(data.c) && data.c.join('').length; + cb(null, size || 0); + }); }); }; @@ -451,11 +478,23 @@ define([ }); }; - common.isNewChannel = function (href, password, cb) { - postMessage('IS_NEW_CHANNEL', {href: href, password: password}, function (obj) { - if (obj.error) { return void cb(obj.error); } - if (!obj) { return void cb('INVALID_RESPONSE'); } - cb(undefined, obj.isNew); + common.isNewChannel = function (href, password, _cb) { + var cb = Util.once(Util.mkAsync(_cb)); + var channel = Hash.hrefToHexChannelId(href, password); + var error; + 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; } + waitFor.abort(); + cb(undefined, obj.isNew); + })); + }).nThen(function () { + Cache.getChannelCache(channel, function(err, data) { + if (err || !data) { return void cb(error); } + cb(null, false); + }); }); }; diff --git a/www/common/inner/common-mediatag.js b/www/common/inner/common-mediatag.js index 3d28d126a..2ca28bf4c 100644 --- a/www/common/inner/common-mediatag.js +++ b/www/common/inner/common-mediatag.js @@ -76,7 +76,7 @@ define([ MT.displayAvatar = function (common, $container, href, name, _cb) { var cb = Util.once(Util.mkAsync(_cb || function () {})); var displayDefault = function () { - var text = (href && typeof(href) === "string") ? href : Util.getFirstCharacter(name); + var text = Util.getFirstCharacter(name || Messages.anonymous); var $avatar = $('', {'class': 'cp-avatar-default'}).text(text); $container.append($avatar); if (cb) { cb(); } diff --git a/www/common/messenger-ui.js b/www/common/messenger-ui.js index da90db12c..76a756a11 100644 --- a/www/common/messenger-ui.js +++ b/www/common/messenger-ui.js @@ -1037,6 +1037,7 @@ define([ //}); execCommand('GET_MY_INFO', null, function (e, info) { + if (e) { return; } contactsData[info.curvePublic] = info; }); diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index c91106d8c..5715038cc 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -91,7 +91,7 @@ define([ Realtime.whenRealtimeSyncs(s.sharedFolders[k].realtime, waitFor()); } } - }).nThen(function () { console.log('done');cb(); }); + }).nThen(function () { cb(); }); }; Store.get = function (clientId, data, cb) { @@ -1035,9 +1035,6 @@ define([ }); }; Store.setPadTitle = function (clientId, data, cb) { - if (store.offline) { - return void cb({ error: 'OFFLINE' }); - } var title = data.title; var href = data.href; var channel = data.channel; @@ -1110,6 +1107,11 @@ define([ Array.prototype.push.apply(allData, res); }); var contains = allData.length !== 0; + if (store.offline && !contains) { + return void cb({ error: 'OFFLINE' }); + } else if (store.offline) { + return void cb(); + } allData.forEach(function (obj) { var pad = obj.data; pad.atime = +new Date(); @@ -1689,7 +1691,7 @@ define([ noChainPad: true, channel: data.channel, metadata: data.metadata, - network: store.network, + network: store.network || store.networkPromise, //readOnly: data.readOnly, onConnect: function (wc, sendMessage) { channel.sendMessage = function (msg, cId, cb) { diff --git a/www/common/sframe-app-outer.js b/www/common/sframe-app-outer.js index d85266ca7..ae7ee47c3 100644 --- a/www/common/sframe-app-outer.js +++ b/www/common/sframe-app-outer.js @@ -45,6 +45,7 @@ define([ window.addEventListener('message', onMsg); }).nThen(function (/*waitFor*/) { SFCommonO.start({ + cache: true, hash: hash, href: href, useCreationScreen: true, diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 059dc90e4..4d06b8a1e 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -431,6 +431,10 @@ define([ } if (!stored && !parsed.hashData.password) { // We've received a link without /p/ and it doesn't work without a password: abort + if (e === "ANON_RPC_NOT_READY") { + // We're currently offline and the pad is not in our cache + sframeChan.event('EV_OFFLINE'); + } return void todo(); } // Wrong password or deleted file? diff --git a/www/common/sframe-common.js b/www/common/sframe-common.js index 3eb86ff93..0f823c491 100644 --- a/www/common/sframe-common.js +++ b/www/common/sframe-common.js @@ -15,6 +15,7 @@ define([ '/common/metadata-manager.js', '/customize/application_config.js', + '/common/outer/cache-store.js', '/common/common-realtime.js', '/common/common-util.js', '/common/common-hash.js', @@ -39,6 +40,7 @@ define([ MT, MetadataMgr, AppConfig, + Cache, CommonRealtime, Util, Hash, @@ -167,14 +169,22 @@ define([ }; funcs.getFileSize = function (channelId, cb) { - funcs.sendAnonRpcMsg("GET_FILE_SIZE", channelId, function (data) { - if (!data) { return void cb("No response"); } - if (data.error) { return void cb(data.error); } - if (data.response && data.response.length && typeof(data.response[0]) === 'number') { - return void cb(void 0, data.response[0]); - } else { - cb('INVALID_RESPONSE'); - } + nThen(function (waitFor) { + Cache.getBlobCache(channelId, waitFor(function(err, blob) { + if (err) { return; } + waitFor.abort(); + cb(null, blob.length); + })); + }).nThen(function () { + funcs.sendAnonRpcMsg("GET_FILE_SIZE", channelId, function (data) { + if (!data) { return void cb("No response"); } + if (data.error) { return void cb(data.error); } + if (data.response && data.response.length && typeof(data.response[0]) === 'number') { + return void cb(void 0, data.response[0]); + } else { + cb('INVALID_RESPONSE'); + } + }); }); }; @@ -678,6 +688,10 @@ 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); }); diff --git a/www/common/toolbar.js b/www/common/toolbar.js index 0927cde21..c25f7dfe9 100644 --- a/www/common/toolbar.js +++ b/www/common/toolbar.js @@ -365,6 +365,9 @@ MessengerUI, Messages) { if (!toolbar.connected) { return; } updateUserList(toolbar, config); }); + setTimeout(function () { + updateUserList(toolbar, config, true); + }); } }; @@ -1246,8 +1249,8 @@ MessengerUI, Messages) { if (typeof el !== "string" || !el.trim()) { return; } if (typeof tb[el] === "function") { if (!init && config.displayed.indexOf(el) !== -1) { return; } // Already done - toolbar[el] = tb[el](toolbar, config); if (!init) { config.displayed.push(el); } + toolbar[el] = tb[el](toolbar, config); } }); checkSize(); From e629f0aa470364280bc35f360bf27e6fd2695ed2 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 9 Nov 2020 13:29:16 +0100 Subject: [PATCH 5/8] Offline fixes --- www/common/common-ui-elements.js | 1 + www/common/inner/access.js | 7 +++++++ www/common/inner/properties.js | 3 +++ www/common/inner/share.js | 24 +++++++++++++++++++++--- www/common/outer/async-store.js | 2 ++ www/common/sframe-common-outer.js | 9 +++++---- 6 files changed, 39 insertions(+), 7 deletions(-) diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index e977f1f49..706a8e305 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -229,6 +229,7 @@ define([ }; }; + UIElements.noContactsMessage = function (common) { var metadataMgr = common.getMetadataMgr(); var data = metadataMgr.getUserData(); diff --git a/www/common/inner/access.js b/www/common/inner/access.js index c0967c139..fcb07a4b4 100644 --- a/www/common/inner/access.js +++ b/www/common/inner/access.js @@ -769,6 +769,7 @@ define([ }, function () {}); }; + Messages.access_offline = "You're currently offline. Access management is not available"; // XXX var getAccessTab = function (Env, data, opts, _cb) { var cb = Util.once(Util.mkAsync(_cb)); var common = Env.common; @@ -776,8 +777,14 @@ define([ var sframeChan = common.getSframeChannel(); var metadataMgr = common.getMetadataMgr(); + var priv = metadataMgr.getPrivateData(); var $div = $(h('div.cp-share-columns')); + + if (priv.offline) { + $div.append(h('p', Messages.access_offline)); + return void cb(void 0, $div); + } if (!data) { return void cb(void 0, $div); } var div1 = h('div.cp-usergrid-user.cp-share-column.cp-access'); diff --git a/www/common/inner/properties.js b/www/common/inner/properties.js index 58f10e738..46d570d3e 100644 --- a/www/common/inner/properties.js +++ b/www/common/inner/properties.js @@ -47,6 +47,9 @@ define([ if (!common.isLoggedIn()) { return void cb(void 0, $d); } + var privateData = common.getMetadataMgr().getPrivateData(); + if (privateData.offline) { return void cb(void 0, $d); } + // File and history size... var owned = Modal.isOwned(Env, data); diff --git a/www/common/inner/share.js b/www/common/inner/share.js index f3ca53f75..b70a71fa2 100644 --- a/www/common/inner/share.js +++ b/www/common/inner/share.js @@ -273,6 +273,21 @@ define([ var hasFriends = opts.hasFriends; var onFriendShare = Util.mkEvent(); + Messages.share_noContactsOffline = "OFFLINE"; // XXX + var metadataMgr = common.getMetadataMgr(); + var priv = metadataMgr.getPrivateData(); + if (priv.offline) { + return void cb(void 0, { + content: h('p', Messages.share_noContactsOffline), + buttons: [{ + className: 'cancel', + name: Messages.filePicker_close, + onClick: function () {}, + keys: [27] + }] + }); + } + var friendsObject = hasFriends ? createShareWithFriends(opts, onFriendShare, opts.getLinkValue) : UIElements.noContactsMessage(common); var friendsList = friendsObject.content; @@ -642,6 +657,8 @@ define([ opts.teams = teams; var hasFriends = opts.hasFriends = Object.keys(opts.friends || {}).length || Object.keys(teams).length; + var metadataMgr = common.getMetadataMgr(); + var priv = metadataMgr.getPrivateData(); // check if the pad is password protected var pathname = opts.pathname; @@ -662,23 +679,24 @@ define([ $rights.find('input[type="radio"]').trigger('change'); }; var onShowContacts = function () { - if (!hasFriends) { + if (!hasFriends || priv.offline) { $rights.hide(); } }; + var contactsActive = hasFriends && !priv.offline; var tabs = [{ getTab: getContactsTab, title: Messages.share_contactCategory, icon: "fa fa-address-book", - active: hasFriends, + active: contactsActive, onShow: onShowContacts, onHide: resetTab }, { getTab: getLinkTab, title: Messages.share_linkCategory, icon: "fa fa-link", - active: !hasFriends, + active: !contactsActive, }, { getTab: getEmbedTab, title: Messages.share_embedCategory, diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 5715038cc..57f31905a 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1924,6 +1924,7 @@ define([ Store.getPadMetadata = function (clientId, data, _cb) { var cb = Util.once(Util.mkAsync(_cb)); + if (store.offline || !store.anon_rpc) { return void cb({ error: 'OFFLINE' }); } if (!data.channel) { return void cb({ error: 'ENOTFOUND'}); } if (data.channel.length !== 32) { return void cb({ error: 'EINVAL'}); } store.anon_rpc.send('GET_METADATA', data.channel, function (err, obj) { @@ -2605,6 +2606,7 @@ define([ // "cb" may have already been called by onCacheReady if (typeof(cb) === 'function') { cb(returned); } sendDriveEvent('NETWORK_RECONNECT'); + broadcast([], "UPDATE_METADATA"); store.offline = false; // XXX broadcast READY event with the missing data // XXX we can improve feedback to queue the queries and send them when coming back online diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 4d06b8a1e..3238db7f0 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -431,12 +431,13 @@ define([ } if (!stored && !parsed.hashData.password) { // We've received a link without /p/ and it doesn't work without a password: abort - if (e === "ANON_RPC_NOT_READY") { - // We're currently offline and the pad is not in our cache - sframeChan.event('EV_OFFLINE'); - } 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); })); From cebe1252f80e0690ad64bc0fe937e947c60625bd Mon Sep 17 00:00:00 2001 From: yflory Date: Tue, 10 Nov 2020 16:50:38 +0100 Subject: [PATCH 6/8] Fix workers submodules after reconnecting --- www/common/outer/async-store.js | 41 ++++++++++++++++++++------------- www/drive/inner.js | 1 - 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 57f31905a..720c0947e 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1378,13 +1378,16 @@ define([ // Universal Store.universal = { execCommand: function (clientId, obj, cb) { - var type = obj.type; - var data = obj.data; - if (store.modules[type]) { - store.modules[type].execCommand(clientId, data, cb); - } else { - return void cb({error: type + ' is disabled'}); - } + onReadyEvt.reg(function () { + var type = obj.type; + var data = obj.data; + if (store.modules[type]) { + console.error(obj); + store.modules[type].execCommand(clientId, data, cb); + } else { + return void cb({error: type + ' is disabled'}); + } + }); } }; var loadUniversal = function (Module, type, waitFor, clientId) { @@ -1424,17 +1427,23 @@ define([ // Cursor Store.cursor = { execCommand: function (clientId, data, cb) { - if (!store.cursor) { return void cb ({error: 'Cursor channel is disabled'}); } - store.cursor.execCommand(clientId, data, cb); + // The cursor module can only be used when the store is ready + onReadyEvt.reg(function () { + if (!store.cursor) { return void cb ({error: 'Cursor channel is disabled'}); } + store.cursor.execCommand(clientId, data, cb); + }); } }; // Mailbox Store.mailbox = { execCommand: function (clientId, data, cb) { - if (!store.loggedIn) { return void cb(); } - if (!store.mailbox) { return void cb ({error: 'Mailbox is disabled'}); } - store.mailbox.execCommand(clientId, data, cb); + // The mailbox can only be used when the store is ready + onReadyEvt.reg(function () { + if (!store.loggedIn) { return void cb(); } + if (!store.mailbox) { return void cb ({error: 'Mailbox is disabled'}); } + store.mailbox.execCommand(clientId, data, cb); + }); } }; @@ -2568,8 +2577,8 @@ define([ loadUniversal(History, 'history', waitFor); cleanFriendRequests(); }).nThen(function () { - var requestLogin = function () { - broadcast([], "REQUEST_LOGIN"); + var requestLogin = function () { + broadcast([], "REQUEST_LOGIN"); }; if (store.loggedIn) { @@ -2664,9 +2673,9 @@ define([ broadcast([], "UPDATE_TOKEN", { token: proxy[Constants.tokenKey] }); }); - onReadyEvt.fire(); - loadMailbox(); + + onReadyEvt.fire(); }); }; diff --git a/www/drive/inner.js b/www/drive/inner.js index 364d4a9d9..d4ac14746 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -310,7 +310,6 @@ define([ onDisconnect(); }); sframeChan.on('EV_NETWORK_RECONNECT', function () { - console.log('here'); onReconnect(); }); common.onLogout(function () { setEditable(false); }); From 90b4b0459e79691a3fd0226d79a259f9e5bac1dc Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 12 Nov 2020 17:20:48 +0100 Subject: [PATCH 7/8] Don't notify for old mailbox messages --- www/common/outer/mailbox.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/common/outer/mailbox.js b/www/common/outer/mailbox.js index 11c912a44..803d8bc93 100644 --- a/www/common/outer/mailbox.js +++ b/www/common/outer/mailbox.js @@ -297,6 +297,7 @@ proxy.mailboxes = { msg: msg, hash: hash }; + var notify = box.ready; Handlers.add(ctx, box, message, function (dismissed, toDismiss) { if (toDismiss) { // List of other messages to remove dismiss(ctx, toDismiss, '', function () { @@ -314,8 +315,7 @@ proxy.mailboxes = { } box.content[hash] = msg; showMessage(ctx, type, message, null, function (obj) { - if (!box.ready) { return; } - if (!obj || !obj.msg) { return; } + if (!obj || !obj.msg || !notify) { return; } Notify.system(undefined, obj.msg); }); }); From 93f6619a3e4abda5067cc0a67ff6bb4d52b448d9 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 12 Nov 2020 17:35:57 +0100 Subject: [PATCH 8/8] Fix team always offline --- www/common/outer/async-store.js | 1 - www/teams/inner.js | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 720c0947e..6ab91edd1 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1382,7 +1382,6 @@ define([ var type = obj.type; var data = obj.data; if (store.modules[type]) { - console.error(obj); store.modules[type].execCommand(clientId, data, cb); } else { return void cb({error: type + ' is disabled'}); diff --git a/www/teams/inner.js b/www/teams/inner.js index 070d11825..fb7952a98 100644 --- a/www/teams/inner.js +++ b/www/teams/inner.js @@ -328,6 +328,7 @@ define([ }, true); } + driveAPP.online = !teamData.offline; var drive = DriveUI.create(common, { proxy: proxy, folders: folders,