diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ca6c0f0a..7201173cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,11 @@ To update from 3.19.1 to 3.20.0: * Remote changes in the Kanban app removed pending text in new cards, effectively making it impossible (and very frustrating) to create new cards while anyone else was editing existing content or submitting their own new cards. * Dropping an image directly into a spreadsheet no longer puts the UI into an unrecoverable state, though we still don't support image drop. To insert images, use the "Insert" menu. This was actually fixed in our 3.19.1 release, but it wasn't documented in the release notes. +* When a user attempted to open an automatically expiring document which had passed its expiration date they were shown a general message indicating that the document had been deleted even when they had sufficient information to know that it had been marked for expiration. We now display a message indicating the more likely cause of its deletion. +* We've spent some time working on the usability of comments in our rich text app: + * When a user started adding a first comment to a document then canceled their action it was possible for the document to get stuck in an odd layout. This extra space allocated towards comments now correctly collapses as intended when there are no comments, pending or otherwise. + * The comments UI is now completely disabled whenever the document is in read-only mode, whether due to disconnection or insufficient permissions. + * The _comment_ button in the app toolbar now toggles on and off to indicate the eligibility of the current selection as a new comment. * We've fixed a number of issues with teams: * Users no longer send themselves a notification when they remove themself as an owner of a pad from within the _Teams_ UI. * The _worker_ process which is responsible for managing account rights now correctly upgrades and downgrades its internal state when its role within a team is changed by a remote user instead of requiring a complete worker reload. diff --git a/customize.dist/src/less2/include/drive.less b/customize.dist/src/less2/include/drive.less index e1790787f..66a936c22 100644 --- a/customize.dist/src/less2/include/drive.less +++ b/customize.dist/src/less2/include/drive.less @@ -317,6 +317,7 @@ padding: 0 10px; border: 0; color: lighten(@colortheme_sidebar-left-fg, 40%); + height: auto; } & > span.cp-app-drive-element-row { overflow: hidden; @@ -575,6 +576,7 @@ border-radius: 0; border: 1px solid #ddd; font-size: 14px; + height: auto; } .cp-app-drive-element-state { position: absolute; @@ -638,6 +640,7 @@ padding: 0 4px; flex: 1; min-width: 0; + height: auto; } &> span { diff --git a/www/common/drive-ui.js b/www/common/drive-ui.js index 0d3e49169..be0bea14a 100644 --- a/www/common/drive-ui.js +++ b/www/common/drive-ui.js @@ -2904,10 +2904,12 @@ define([ var el = useId ? _el : root[_el]; var sfId = (el && el.root && el.key) ? el.root[el.key] : el; if (folder && el && manager.isSharedFolder(sfId)) { - var title = manager.getSharedFolderData(sfId).title || el; + var sfData = manager.getSharedFolderData(sfId); + var title = sfData.title || sfData.lastTitle || el; return String(title).toLowerCase(); } else if (folder) { - return String((el && el.key) || el).toLowerCase(); + console.log(el); + return String((el && el.key) || _el).toLowerCase(); } var data = manager.getFileData(el); if (!data) { return ''; } @@ -2928,6 +2930,7 @@ define([ } props[uid] = getProp(k); }); + if (folder) { console.error(useId, props); } keys.sort(function(a, b) { var _a = props[(a && a.uid) || a]; var _b = props[(b && b.uid) || b]; diff --git a/www/common/inner/access.js b/www/common/inner/access.js index 84ada6da6..2e1f4b4a8 100644 --- a/www/common/inner/access.js +++ b/www/common/inner/access.js @@ -898,7 +898,7 @@ define([ } } if (owned) { - var deleteOwned = h('button.btn.btn-danger-alt', Messages.fc_delete_owned); + var deleteOwned = h('button.btn.btn-danger-alt', [h('i.cptools.cptools-destroy'), Messages.fc_delete_owned]); var spinner = UI.makeSpinner(); UI.confirmButton(deleteOwned, { classes: 'btn-danger' @@ -909,6 +909,7 @@ define([ channel: data.channel }, function (err, obj) { spinner.done(); + UI.findCancelButton().click(); if (err || (obj && obj.error)) { UI.warn(Messages.error); } }); }); diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index b5be30fa1..521b37bbe 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -2054,6 +2054,10 @@ define([ var addSharedFolderHandler = function () { store.sharedFolders = {}; store.handleSharedFolder = function (id, rt) { + if (!rt) { + delete store.sharedFolders[id]; + return; + } store.sharedFolders[id] = rt; if (store.driveEvents) { registerProxyEvents(rt.proxy, id); diff --git a/www/common/outer/sharedfolder.js b/www/common/outer/sharedfolder.js index 3c85492f9..f02ea9c5a 100644 --- a/www/common/outer/sharedfolder.js +++ b/www/common/outer/sharedfolder.js @@ -211,6 +211,9 @@ define([ // We can only hide it sf.teams.forEach(function (obj) { obj.store.manager.deprecateProxy(obj.id, secret.channel); + if (obj.store.handleSharedFolder) { + obj.store.handleSharedFolder(obj.id, null); + } }); } catch (e) {} delete allSharedFolders[secret.channel]; @@ -252,9 +255,14 @@ define([ if (!sf) { return; } var clients = sf.teams; if (!Array.isArray(clients)) { return; } + // Remove the shared folder from the client's store and + // remove the client/team from our list var idx; clients.some(function (obj, i) { if (obj.store.id === teamId) { + if (obj.store.handleSharedFolder) { + obj.store.handleSharedFolder(obj.id, null); + } idx = i; return true; } diff --git a/www/common/outer/team.js b/www/common/outer/team.js index 9e59410db..7c3398189 100644 --- a/www/common/outer/team.js +++ b/www/common/outer/team.js @@ -164,6 +164,10 @@ define([ var handleSharedFolder = function (ctx, id, sfId, rt) { var t = ctx.teams[id]; if (!t) { return; } + if (!rt) { + delete t.sharedFolders[sfId]; + return; + } t.sharedFolders[sfId] = rt; registerChangeEvents(ctx, t, rt.proxy, sfId); }; diff --git a/www/common/proxy-manager.js b/www/common/proxy-manager.js index 7211df88c..bf7af8d39 100644 --- a/www/common/proxy-manager.js +++ b/www/common/proxy-manager.js @@ -52,6 +52,10 @@ define([ // Password may have changed var deprecateProxy = function (Env, id, channel) { + if (Env.folders[id] && Env.folders[id].deleting) { + // Folder is being deleted by its owner, don't deprecate it + return; + } if (Env.user.userObject.readOnly) { // In a read-only team, we can't deprecate a shared folder // Use a empty object with a deprecated flag... @@ -823,19 +827,38 @@ define([ var data = uo.isFile(el) ? uo.getFileData(el) : getSharedFolderData(Env, el); chan = data.channel; } + // If the pad was a shared folder, delete it too and leave it + var fId; + Object.keys(Env.user.proxy[UserObject.SHARED_FOLDERS] || {}).some(function (id) { + var sfData = Env.user.proxy[UserObject.SHARED_FOLDERS][id] || {}; + if (sfData.channel === chan) { + fId = Number(id); + Env.folders[id].deleting = true; + return true; + } + }); Env.removeOwnedChannel(chan, function (obj) { // If the error is that the file is already removed, nothing to // report, it's a normal behavior (pad expired probably) if (obj && obj.error && obj.error.code !== "ENOENT") { // RPC may not be responding // Send a report that can be handled manually + if (fId && Env.folders[fId] && Env.folders[fId].deleting) { + delete Env.folders[fId].deleting; + } console.error(obj.error, chan); Feedback.send('ERROR_DELETING_OWNED_PAD=' + chan + '|' + obj.error, true); return void cb(); } - // No error: delete the pads and all its copies from our drive and shared folders + // No error: delete the pad and all its copies from our drive and shared folders var ids = _findChannels(Env, [chan]); + + // If the pad was a shared folder, delete it too and leave it + if (fId) { + ids.push(fId); + } + ids.forEach(function (id) { var paths = findFile(Env, id); var _resolved = _resolvePaths(Env, paths); @@ -869,6 +892,10 @@ define([ }).nThen(function () { // Remove deleted pads from the drive _delete(Env, { resolved: toDelete }, cb); + // If we were using the access modal, send a refresh command + if (data.channel) { + Env.Store.refreshDriveUI(); + } }); }; @@ -926,9 +953,8 @@ define([ if (!resolved.id) { var el = Env.user.userObject.find(resolved.path); if (Env.user.userObject.isSharedFolder(el) && Env.folders[el]) { - var oldName = Env.folders[el].proxy.metadata.title; Env.folders[el].proxy.metadata.title = data.newName; - Env.user.proxy[UserObject.SHARED_FOLDERS][el].lastTitle = oldName; + Env.user.proxy[UserObject.SHARED_FOLDERS][el].lastTitle = data.newName; return void cb(); } }