diff --git a/customize.dist/loading.js b/customize.dist/loading.js index 4f8b79125..733b3fff0 100644 --- a/customize.dist/loading.js +++ b/customize.dist/loading.js @@ -327,6 +327,7 @@ button.primary:hover{ if (!built) { return; } try { var node = document.querySelector('.cp-loading-progress'); + if (!node) { return; } if (node.parentNode) { node.parentNode.removeChild(node); } document.querySelector('.cp-loading-spinner-container').setAttribute('style', 'display:none;'); document.querySelector('#cp-loading-message').setAttribute('style', 'display:block;'); diff --git a/www/common/drive-ui.js b/www/common/drive-ui.js index 53e496ef4..4886b213e 100644 --- a/www/common/drive-ui.js +++ b/www/common/drive-ui.js @@ -42,6 +42,7 @@ define([ var APP = window.APP = { editable: false, + online: true, mobile: function () { if (window.matchMedia) { return !window.matchMedia('(any-pointer:fine)').matches; } else { return $('body').width() <= 600; } @@ -267,13 +268,25 @@ define([ }; // Handle disconnect/reconnect - var setEditable = function (state, isHistory) { + // If isHistory and isSf are both false, update the "APP.online" flag + // If isHistory is true, update the "APP.history" flag + // isSf is used to detect offline shared folders: setEditable is called on displayDirectory + var setEditable = function (state, isHistory, isSf) { if (APP.closed || !APP.$content || !$.contains(document.documentElement, APP.$content[0])) { return; } + if (isHistory) { + APP.history = !state; + } else if (!isSf) { + APP.online = state; + } + state = APP.online && !APP.history && state; APP.editable = !APP.readOnly && state; + if (!state) { APP.$content.addClass('cp-app-drive-readonly'); - if (!isHistory) { + if (!APP.history || !APP.online) { $('#cp-app-drive-connection-state').show(); + } else { + $('#cp-app-drive-connection-state').hide(); } $('[draggable="true"]').attr('draggable', false); } @@ -3670,6 +3683,15 @@ define([ } var readOnlyFolder = false; + + // If the shared folder is offline, add the "DISCONNECTED" banner, otherwise + // use the normal "editable" behavior (based on drive offline or history mode) + if (sfId && manager.folders[sfId].offline) { + setEditable(false, false, true); + } else { + setEditable(true, false, true); + } + if (APP.readOnly) { // Read-only drive (team?) $content.prepend($readOnly.clone()); diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 599d1d3b3..7066d3921 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -121,10 +121,13 @@ define([ Store.getSharedFolder = function (clientId, data, cb) { var s = getStore(data.teamId); var id = data.id; + var proxy; if (!s || !s.manager) { return void cb({ error: 'ENOTFOUND' }); } if (s.manager.folders[id]) { + proxy = Util.clone(s.manager.folders[id].proxy); + proxy.offline = Boolean(s.manager.folders[id].offline); // If it is loaded, return the shared folder proxy - return void cb(s.manager.folders[id].proxy); + return void cb(proxy); } else { // Otherwise, check if we know this shared folder var shared = Util.find(s.proxy, ['drive', UserObject.SHARED_FOLDERS]) || {}; diff --git a/www/common/outer/team.js b/www/common/outer/team.js index dc078a2f4..f23fb8a18 100644 --- a/www/common/outer/team.js +++ b/www/common/outer/team.js @@ -58,11 +58,11 @@ define([ }); proxy.on('disconnect', function () { team.offline = true; - team.sendEvent('NETWORK_DISCONNECT'); + team.sendEvent('NETWORK_DISCONNECT', team.id); }); proxy.on('reconnect', function () { team.offline = false; - team.sendEvent('NETWORK_RECONNECT'); + team.sendEvent('NETWORK_RECONNECT', team.id); }); } proxy.on('change', [], function (o, n, p) { @@ -934,7 +934,9 @@ define([ if (!team) { return void cb ({error: 'ENOENT'}); } if (!team.roster) { return void cb({error: 'NO_ROSTER'}); } var state = team.roster.getState() || {}; - cb(state.metadata || {}); + var md = state.metadata || {}; + md.offline = team.offline; + cb(md); }; var setTeamMetadata = function (ctx, data, cId, cb) { @@ -1882,15 +1884,15 @@ define([ var t = Util.clone(teams); Object.keys(t).forEach(function (id) { // If failure to load the team, don't send it - if (ctx.teams[id]) { return; } + if (ctx.teams[id]) { + t[id].offline = ctx.teams[id].offline; + return; + } t[id].error = true; }); cb(t); }; team.execCommand = function (clientId, obj, cb) { - if (ctx.store.offline) { - return void cb({ error: 'OFFLINE' }); - } var cmd = obj.cmd; var data = obj.data; @@ -1914,30 +1916,36 @@ define([ return void setTeamMetadata(ctx, data, clientId, cb); } if (cmd === 'OFFER_OWNERSHIP') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void offerOwnership(ctx, data, clientId, cb); } if (cmd === 'ANSWER_OWNERSHIP') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void answerOwnership(ctx, data, clientId, cb); } if (cmd === 'DESCRIBE_USER') { return void describeUser(ctx, data, clientId, cb); } if (cmd === 'INVITE_TO_TEAM') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void inviteToTeam(ctx, data, clientId, cb); } if (cmd === 'LEAVE_TEAM') { return void leaveTeam(ctx, data, clientId, cb); } if (cmd === 'JOIN_TEAM') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void joinTeam(ctx, data, clientId, cb); } if (cmd === 'REMOVE_USER') { return void removeUser(ctx, data, clientId, cb); } if (cmd === 'DELETE_TEAM') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void deleteTeam(ctx, data, clientId, cb); } if (cmd === 'CREATE_TEAM') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void createTeam(ctx, data, clientId, cb); } if (cmd === 'GET_EDITABLE_FOLDERS') { @@ -1950,6 +1958,7 @@ define([ return void getPreviewContent(ctx, data, clientId, cb); } if (cmd === 'ACCEPT_LINK_INVITATION') { + if (ctx.store.offline) { return void cb({ error: 'OFFLINE' }); } return void acceptLinkInvitation(ctx, data, clientId, cb); } }; diff --git a/www/common/proxy-manager.js b/www/common/proxy-manager.js index 88f4a052b..74bfcd890 100644 --- a/www/common/proxy-manager.js +++ b/www/common/proxy-manager.js @@ -40,6 +40,14 @@ define([ userObject: userObject, leave: leave }; + if (proxy.on) { + proxy.on('disconnect', function () { + Env.folders[id].offline = true; + }); + proxy.on('reconnect', function () { + Env.folders[id].online = true; + }); + } return userObject; }; diff --git a/www/drive/inner.js b/www/drive/inner.js index f85819083..9aa080dc3 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -82,6 +82,8 @@ define([ var readOnly = !secret.keys.editKeyStr; if (!manager || !manager.folders[fId]) { return; } manager.folders[fId].userObject.setReadOnly(readOnly, secret.keys.secondaryKey); + + manager.folders[fId].offline = newObj.offline; })); }); // Remove from memory folders that have been deleted from the drive remotely diff --git a/www/teams/inner.js b/www/teams/inner.js index a75ed94db..070d11825 100644 --- a/www/teams/inner.js +++ b/www/teams/inner.js @@ -46,7 +46,9 @@ define([ Backup, Messages) { - var APP = {}; + var APP = { + teams: {} + }; var driveAPP = {}; var saveAs = window.saveAs; //var SHARED_FOLDER_NAME = Messages.fm_sharedFolderName; @@ -91,6 +93,8 @@ define([ var readOnly = !secret.keys.editKeyStr; if (!manager || !manager.folders[fId]) { return; } manager.folders[fId].userObject.setReadOnly(readOnly, secret.keys.secondaryKey); + + manager.folders[fId].offline = newObj.offline; })); }); // Remove from memory folders that have been deleted from the drive remotely @@ -211,6 +215,11 @@ define([ if (obj && obj.error) { return void UI.warn(Messages.error); } + + // Refresh offline state + APP.teams[APP.team] = APP.teams[APP.team] || {}; + APP.teams[APP.team].offline = obj.offline; + common.displayAvatar($avatar, obj.avatar, obj.name); $category.append($avatar); $avatar.append(h('span.cp-sidebarlayout-category-name', obj.name)); @@ -333,6 +342,11 @@ define([ }); APP.drive = drive; driveAPP.refresh = drive.refresh; + + if (APP.teams[id] && APP.teams[id].offline) { + setEditable(false); + drive.refresh(); + } }); }; @@ -406,7 +420,15 @@ define([ content.push(h('h3', Messages.team_listTitle + ' ' + slots)); + APP.teams = {}; + keys.forEach(function (id) { + if (!obj[id].empty) { + APP.teams[id] = { + offline: obj[id] && obj[id].offline + }; + } + var team = obj[id]; if (team.empty) { list.push(h('div.cp-team-list-team.empty', [ @@ -1433,13 +1455,15 @@ define([ } }); - var onDisconnect = function (noAlert) { + var onDisconnect = function (teamId) { + if (APP.team && teamId && APP.team !== teamId) { return; } setEditable(false); if (APP.team && driveAPP.refresh) { driveAPP.refresh(); } toolbar.failed(); - if (!noAlert) { UIElements.disconnectAlert(); } + UIElements.disconnectAlert(); }; - var onReconnect = function () { + var onReconnect = function (teamId) { + if (APP.team && teamId && APP.team !== teamId) { return; } setEditable(true); if (APP.team && driveAPP.refresh) { driveAPP.refresh(); } toolbar.reconnecting(); @@ -1449,11 +1473,17 @@ define([ sframeChan.on('EV_DRIVE_LOG', function (msg) { UI.log(msg); }); - sframeChan.on('EV_NETWORK_DISCONNECT', function () { - onDisconnect(); + sframeChan.on('EV_NETWORK_DISCONNECT', function (teamId) { + onDisconnect(teamId); + if (teamId && APP.teams[teamId]) { + APP.teams[teamId].offline = true; + } }); - sframeChan.on('EV_NETWORK_RECONNECT', function () { - onReconnect(); + sframeChan.on('EV_NETWORK_RECONNECT', function (teamId) { + onReconnect(teamId); + if (teamId && APP.teams[teamId]) { + APP.teams[teamId].offline = false; + } }); common.onLogout(function () { setEditable(false); }); }); diff --git a/www/teams/main.js b/www/teams/main.js index cab84d160..0344a902f 100644 --- a/www/teams/main.js +++ b/www/teams/main.js @@ -85,10 +85,10 @@ define([ sframeChan.event('EV_'+obj.data.ev, obj.data.data); } if (obj.data.ev === 'NETWORK_RECONNECT') { - sframeChan.event('EV_NETWORK_RECONNECT'); + sframeChan.event('EV_NETWORK_RECONNECT', obj.data.data); } if (obj.data.ev === 'NETWORK_DISCONNECT') { - sframeChan.event('EV_NETWORK_DISCONNECT'); + sframeChan.event('EV_NETWORK_DISCONNECT', obj.data.data); } });