diff --git a/NEWS b/NEWS index 2e97bf2e8..824030ec8 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ New features Enhancements - [eas] use the preferred email identity in EAS if valid (#3698) - [eas] handle inline attachments during EAS content generation + - [web] all batch operations can now be performed on selected messages in advanced search mode Bug fixes - [web] fixed crash when an attachment filename has no extension diff --git a/UI/Templates/MailerUI/UIxMailFolderTemplate.wox b/UI/Templates/MailerUI/UIxMailFolderTemplate.wox index 098ccda08..9967153fb 100644 --- a/UI/Templates/MailerUI/UIxMailFolderTemplate.wox +++ b/UI/Templates/MailerUI/UIxMailFolderTemplate.wox @@ -65,11 +65,11 @@
+ ng-click="mailbox.searchMode()"> search {{mailbox.selectedFolder.name}} + ng-click="mailbox.searchMode()">{{mailbox.selectedFolder.name}} @@ -128,7 +128,7 @@ arrow_back - +
diff --git a/UI/WebServerResources/js/Mailer/Mailbox.service.js b/UI/WebServerResources/js/Mailer/Mailbox.service.js index ed4bd8e32..5112be4a1 100644 --- a/UI/WebServerResources/js/Mailer/Mailbox.service.js +++ b/UI/WebServerResources/js/Mailer/Mailbox.service.js @@ -221,6 +221,16 @@ return Mailbox.$absolutePath(this.$account.id, this.path); }; + /** + * @function $selectedMessages + * @memberof Mailbox.prototype + * @desc Return the messages selected by the user. + * @returns Message instances + */ + Mailbox.prototype.$selectedMessages = function() { + return _.filter(this.$messages, function(message) { return message.selected; }); + }; + /** * @function $selectedCount * @memberof Mailbox.prototype @@ -228,13 +238,7 @@ * @returns the number of selected messages */ Mailbox.prototype.$selectedCount = function() { - var count; - - count = 0; - if (this.$messages) { - count = (_.filter(this.$messages, function(message) { return message.selected; })).length; - } - return count; + return this.$selectedMessages().length; }; /** @@ -542,12 +546,14 @@ * @desc Add or remove a flag on a message set * @returns a promise of the HTTP operation */ - Mailbox.prototype.$flagMessages = function(uids, flags, operation) { - var data = {msgUIDs: uids, + Mailbox.prototype.$flagMessages = function(messages, flags, operation) { + var data = {msgUIDs: _.map(messages, 'uid'), flags: flags, operation: operation}; - return Mailbox.$$resource.post(this.id, 'addOrRemoveLabel', data); + return Mailbox.$$resource.post(this.id, 'addOrRemoveLabel', data).then(function() { + return messages; + }); }; /** @@ -656,9 +662,9 @@ * @return a promise of the HTTP operation */ Mailbox.prototype.$markOrUnMarkMessagesAsJunk = function(messages) { - var _this = this, uids; - var method = (this.type == 'junk' ? 'markMessagesAsNotJunk' : 'markMessagesAsJunk'); - uids = _.map(messages, 'uid'); + var _this = this, + uids = _.map(messages, 'uid'), + method = (this.type == 'junk' ? 'markMessagesAsNotJunk' : 'markMessagesAsJunk'); return Mailbox.$$resource.post(this.id, method, {uids: uids}); }; @@ -669,8 +675,9 @@ * @desc Copy multiple messages from the current mailbox to a target one * @return a promise of the HTTP operation */ - Mailbox.prototype.$copyMessages = function(uids, folder) { - var _this = this; + Mailbox.prototype.$copyMessages = function(messages, folder) { + var _this = this, + uids = _.map(messages, 'uid'); return Mailbox.$$resource.post(this.id, 'copyMessages', {uids: uids, folder: folder}) .then(function(data) { diff --git a/UI/WebServerResources/js/Mailer/MailboxController.js b/UI/WebServerResources/js/Mailer/MailboxController.js index d27d6dfe1..ab727a564 100644 --- a/UI/WebServerResources/js/Mailer/MailboxController.js +++ b/UI/WebServerResources/js/Mailer/MailboxController.js @@ -6,8 +6,8 @@ /** * @ngInject */ - MailboxController.$inject = ['$window', '$timeout', '$state', '$mdDialog', 'stateAccounts', 'stateAccount', 'stateMailbox', 'encodeUriFilter', 'Dialog', 'Account', 'Mailbox']; - function MailboxController($window, $timeout, $state, $mdDialog, stateAccounts, stateAccount, stateMailbox, encodeUriFilter, Dialog, Account, Mailbox) { + MailboxController.$inject = ['$window', '$timeout', '$q', '$state', '$mdDialog', 'stateAccounts', 'stateAccount', 'stateMailbox', 'encodeUriFilter', 'sgFocus', 'Dialog', 'Account', 'Mailbox']; + function MailboxController($window, $timeout, $q, $state, $mdDialog, stateAccounts, stateAccount, stateMailbox, encodeUriFilter, focus, Dialog, Account, Mailbox) { var vm = this, messageDialog = null; // Expose controller @@ -21,7 +21,12 @@ vm.selectedFolder = stateMailbox; vm.selectMessage = selectMessage; vm.toggleMessageSelection = toggleMessageSelection; - vm.unselectMessages = unselectMessages; + vm.sort = sort; + vm.sortedBy = sortedBy; + vm.searchMode = searchMode; + vm.cancelSearch = cancelSearch; + vm.newMessage = newMessage; + vm.mode = { search: false, multiple: 0 }; vm.confirmDeleteSelectedMessages = confirmDeleteSelectedMessages; vm.markOrUnMarkMessagesAsJunk = markOrUnMarkMessagesAsJunk; vm.copySelectedMessages = copySelectedMessages; @@ -29,160 +34,7 @@ vm.markSelectedMessagesAsFlagged = markSelectedMessagesAsFlagged; vm.markSelectedMessagesAsUnread = markSelectedMessagesAsUnread; vm.selectAll = selectAll; - vm.sort = sort; - vm.sortedBy = sortedBy; - vm.cancelSearch = cancelSearch; - vm.newMessage = newMessage; - vm.mode = { search: false, multiple: 0 }; - - function selectMessage(message) { - if (Mailbox.$virtualMode) - $state.go('mail.account.virtualMailbox.message', {accountId: stateAccount.id, mailboxId: encodeUriFilter(message.$mailbox.path), messageId: message.uid}); - else - $state.go('mail.account.mailbox.message', {messageId: message.uid}); - } - - function toggleMessageSelection($event, message) { - message.selected = !message.selected; - vm.mode.multiple += message.selected? 1 : -1; - $event.preventDefault(); - $event.stopPropagation(); - } - - function unselectMessages() { - _.forEach(vm.selectedFolder.$messages, function(message) { - message.selected = false; - }); - vm.mode.multiple = 0; - } - - function confirmDeleteSelectedMessages() { - Dialog.confirm(l('Warning'), - l('Are you sure you want to delete the selected messages?'), - { ok: l('Delete') }) - .then(function() { - var deleteSelectedMessage = false; - var selectedMessages = _.filter(vm.selectedFolder.$messages, function(message) { - if (message.selected && - message.uid == vm.selectedFolder.selectedMessage) - deleteSelectedMessage = true; - return message.selected; - }); - vm.selectedFolder.$deleteMessages(selectedMessages).then(function(index) { - unselectMessage(deleteSelectedMessage, index); - }); - }); - } - - function markOrUnMarkMessagesAsJunk() { - var moveSelectedMessage = false; - var selectedMessages = _.filter(vm.selectedFolder.$messages, function(message) { - if (message.selected && - message.uid == vm.selectedFolder.selectedMessage) - moveSelectedMessage = true; - return message.selected; - }); - - vm.selectedFolder.$markOrUnMarkMessagesAsJunk(selectedMessages).then(function() { - var folder = '/' + vm.account.id + '/folderINBOX'; - - if (vm.selectedFolder.type != 'junk') { - folder = '/' + vm.account.$getMailboxByType('junk').id; - } - - vm.selectedFolder.$moveMessages(selectedMessages, folder).then(function(index) { - unselectMessage(moveSelectedMessage, index); - }); - }); - } - - function unselectMessage(message, index) { - // Unselect current message and cleverly load the next message - var nextMessage, previousMessage, nextIndex = index; - vm.mode.multiple = vm.selectedFolder.$selectedCount(); - if (message) { - if (Mailbox.$virtualMode) { - $state.go('mail.account.virtualMailbox'); - } - else { - // Select either the next or previous message - if (index > 0) { - nextIndex -= 1; - nextMessage = vm.selectedFolder.$messages[nextIndex]; - } - if (index < vm.selectedFolder.$messages.length) - previousMessage = vm.selectedFolder.$messages[index]; - if (nextMessage) { - if (nextMessage.isread && previousMessage && !previousMessage.isread) { - nextIndex = index; - nextMessage = previousMessage; - } - } - else if (previousMessage) { - nextIndex = index; - nextMessage = previousMessage; - } - if (nextMessage) { - $state.go('mail.account.mailbox.message', { messageId: nextMessage.uid }); - vm.selectedFolder.$topIndex = nextIndex; - } - else { - $state.go('mail.account.mailbox'); - } - } - } - } - - function copySelectedMessages(folder) { - var selectedMessages = _.filter(vm.selectedFolder.$messages, function(message) { return message.selected; }); - var selectedUIDs = _.map(selectedMessages, 'uid'); - vm.selectedFolder.$copyMessages(selectedUIDs, '/' + folder); - } - - function moveSelectedMessages(folder) { - var moveSelectedMessage = false; - var selectedMessages = _.filter(vm.selectedFolder.$messages, function(message) { - if (message.selected && - message.uid == vm.selectedFolder.selectedMessage) - moveSelectedMessage = true; - return message.selected; - }); - vm.selectedFolder.$moveMessages(selectedMessages, '/' + folder).then(function(index) { - unselectMessage(moveSelectedMessage, index); - }); - } - - function selectAll() { - var i = 0, length = vm.selectedFolder.$messages.length; - for (; i < length; i++) - vm.selectedFolder.$messages[i].selected = true; - vm.mode.multiple = length; - } - - function markSelectedMessagesAsFlagged() { - var selectedMessages = _.filter(vm.selectedFolder.$messages, function(message) { return message.selected; }); - var selectedUIDs = _.map(selectedMessages, 'uid'); - - vm.selectedFolder.$flagMessages(selectedUIDs, '\\Flagged', 'add').then(function(d) { - // Success - _.forEach(selectedMessages, function(message) { - message.isflagged = true; - }); - }); - } - - function markSelectedMessagesAsUnread() { - var selectedMessages = _.filter(vm.selectedFolder.$messages, function(message) { return message.selected; }); - var selectedUIDs = _.map(selectedMessages, 'uid'); - - vm.selectedFolder.$flagMessages(selectedUIDs, 'seen', 'remove').then(function(d) { - // Success - _.forEach(selectedMessages, function(message) { - message.isread = false; - vm.selectedFolder.unseenCount++; - }); - }); - } + vm.unselectMessages = unselectMessages; function sort(field) { vm.selectedFolder.$filter({ sort: field }); @@ -192,6 +44,11 @@ return Mailbox.$query.sort == field; } + function searchMode() { + vm.mode.search = true; + focus('search'); + } + function cancelSearch() { vm.mode.search = false; vm.selectedFolder.$filter().then(function() { @@ -228,6 +85,183 @@ }); } } + + function selectMessage(message) { + if (Mailbox.$virtualMode) + $state.go('mail.account.virtualMailbox.message', {mailboxId: encodeUriFilter(message.$mailbox.path), messageId: message.uid}); + else + $state.go('mail.account.mailbox.message', {messageId: message.uid}); + } + + function toggleMessageSelection($event, message) { + message.selected = !message.selected; + vm.mode.multiple += message.selected? 1 : -1; + $event.preventDefault(); + $event.stopPropagation(); + } + + /** + * Batch operations + */ + + function _currentMailboxes() { + if (Mailbox.$virtualMode) + return vm.selectedFolder.$mailboxes; + else + return [vm.selectedFolder]; + } + + function _unselectMessage(message, index) { + // Unselect current message and cleverly load the next message. + // This function must not be called in virtual mode. + var nextMessage, previousMessage, nextIndex = index; + vm.mode.multiple = vm.selectedFolder.$selectedCount(); + if (message) { + // Select either the next or previous message + if (index > 0) { + nextIndex -= 1; + nextMessage = vm.selectedFolder.$messages[nextIndex]; + } + if (index < vm.selectedFolder.$messages.length) + previousMessage = vm.selectedFolder.$messages[index]; + if (nextMessage) { + if (nextMessage.isread && previousMessage && !previousMessage.isread) { + nextIndex = index; + nextMessage = previousMessage; + } + } + else if (previousMessage) { + nextIndex = index; + nextMessage = previousMessage; + } + if (nextMessage) { + vm.selectedFolder.$topIndex = nextIndex; + $state.go('mail.account.mailbox.message', { messageId: nextMessage.uid }); + } + else { + $state.go('mail.account.mailbox'); + } + } + else { + $timeout(function() { + console.warn('go to mailbox'); + $state.go('mail.account.mailbox'); + }); + } + } + + function confirmDeleteSelectedMessages() { + Dialog.confirm(l('Warning'), + l('Are you sure you want to delete the selected messages?'), + { ok: l('Delete') }) + .then(function() { + var deleteSelectedMessage = vm.selectedFolder.hasSelectedMessage(); + var selectedMessages = vm.selectedFolder.$selectedMessages(); + if (_.size(selectedMessages) > 0) + vm.selectedFolder.$deleteMessages(selectedMessages).then(function(index) { + if (Mailbox.$virtualMode) { + // When performing an advanced search, we refresh the view if the selected message + // was deleted, but only once all promises have completed. + if (deleteSelectedMessage) + $state.go('mail.account.virtualMailbox'); + } + else { + // In normal mode, we immediately unselect the selected message. + _unselectMessage(deleteSelectedMessage, index); + } + }); + }); + } + + function markOrUnMarkMessagesAsJunk() { + var moveSelectedMessage = vm.selectedFolder.hasSelectedMessage(); + var selectedMessages = vm.selectedFolder.$selectedMessages(); + if (_.size(selectedMessages) > 0) + vm.selectedFolder.$markOrUnMarkMessagesAsJunk(selectedMessages).then(function() { + var dstFolder = '/' + vm.account.id + '/folderINBOX'; + if (vm.selectedFolder.type != 'junk') { + dstFolder = '/' + vm.account.$getMailboxByType('junk').id; + } + vm.selectedFolder.$moveMessages(selectedMessages, dstFolder).then(function(index) { + if (Mailbox.$virtualMode) { + // When performing an advanced search, we refresh the view if the selected message + // was deleted, but only once all promises have completed. + if (moveSelectedMessage) + $state.go('mail.account.virtualMailbox'); + } + else { + // In normal mode, we immediately unselect the selected message. + _unselectMessage(moveSelectedMessage, index); + } + }); + }); + } + + function copySelectedMessages(dstFolder) { + var selectedMessages = vm.selectedFolder.$selectedMessages(); + if (_.size(selectedMessages) > 0) + vm.selectedFolder.$copyMessages(selectedMessages, '/' + dstFolder); + } + + function moveSelectedMessages(dstFolder) { + var moveSelectedMessage = vm.selectedFolder.hasSelectedMessage(); + var selectedMessages = vm.selectedFolder.$selectedMessages(); + if (_.size(selectedMessages) > 0) + vm.selectedFolder.$moveMessages(selectedMessages, '/' + dstFolder).then(function(index) { + if (Mailbox.$virtualMode) { + // When performing an advanced search, we refresh the view if the selected message + // was moved, but only once all promises have completed. + if (moveSelectedMessage) + $state.go('mail.account.virtualMailbox'); + } + else { + // In normal mode, we immediately unselect the selected message. + _unselectMessage(moveSelectedMessage, index); + } + }); + } + + function selectAll() { + var count = 0; + _.forEach(_currentMailboxes(), function(folder) { + var i = 0, length = folder.$messages.length; + for (; i < length; i++) + folder.$messages[i].selected = true; + count += length; + }); + vm.mode.multiple = count; + } + + function unselectMessages() { + _.forEach(_currentMailboxes(), function(folder) { + _.forEach(folder.$messages, function(message) { + message.selected = false; + }); + }); + vm.mode.multiple = 0; + } + + function markSelectedMessagesAsFlagged() { + var selectedMessages = vm.selectedFolder.$selectedMessages(); + if (_.size(selectedMessages) > 0) + vm.selectedFolder.$flagMessages(selectedMessages, '\\Flagged', 'add').then(function(messages) { + _.forEach(messages, function(message) { + message.isflagged = true; + }); + }); + } + + function markSelectedMessagesAsUnread() { + var selectedMessages = vm.selectedFolder.$selectedMessages(); + if (_.size(selectedMessages) > 0) + vm.selectedFolder.$flagMessages(selectedMessages, 'seen', 'remove').then(function(messages) { + _.forEach(messages, function(message) { + message.isread = false; + message.$mailbox.unseenCount++; + }); + }); + } + } angular diff --git a/UI/WebServerResources/js/Mailer/Message.service.js b/UI/WebServerResources/js/Mailer/Message.service.js index 184aa8261..fa0c1a5f2 100644 --- a/UI/WebServerResources/js/Mailer/Message.service.js +++ b/UI/WebServerResources/js/Mailer/Message.service.js @@ -521,10 +521,19 @@ * @returns a promise of the HTTP operation */ Message.prototype.$reload = function(options) { - var futureMessageData; + var _this = this, futureMessageData; - if (options && options.useCache && this.$futureMessageData) + if (options && options.useCache && this.$futureMessageData) { + if (!this.isread) { + Message.$$resource.fetch(this.$absolutePath(), 'markMessageRead').then(function() { + Message.$timeout(function() { + _this.isread = true; + _this.$mailbox.unseenCount--; + }); + }); + } return this; + } futureMessageData = Message.$$resource.fetch(this.$absolutePath(options), 'view');