(js) Improve message viewer/editor in popup window

This commit is contained in:
Francis Lachapelle
2016-01-07 15:43:26 -05:00
parent 78dfe84d41
commit 68c72f681c
7 changed files with 191 additions and 50 deletions
+1 -1
View File
@@ -12,7 +12,7 @@
const:jsFiles="Common.js, Preferences.services.js, Contacts.services.js, Mailer.app.popup.js, Mailer.services.js, vendor/ckeditor/ckeditor.js, vendor/ckeditor/ck.js, vendor/angular-file-upload.min.js">
<main class="layout-fill" ng-controller="navController">
<div id="detailView" class="view-detail"
<div id="detailView" class="view-detail md-default-theme md-background md-bg md-hue-1"
layout="column" layout-align="start center"
ui-view="message">
<div class="sg-progress-circular-floating">
@@ -23,12 +23,11 @@
ng-click="viewer.close()">
<md-icon>arrow_back</md-icon>
</md-button>
<div class="md-flex hide show-xs"><!-- spacer --></div>
<div class="md-flex"><!-- spacer --></div>
<md-button class="sg-icon-button" label:aria-label="flagged" ng-click="viewer.message.toggleFlag()">
<md-icon ng-class="{'icon-star': viewer.message.isflagged,
'icon-star-border': !viewer.message.isflagged}"><!-- flag --></md-icon>
</md-button>
<div class="md-flex hide-xs"><!-- spacer --></div>
<md-button class="sg-icon-button"
ng-hide="viewer.message.isDraft"
ng-click="viewer.reply($event)"
@@ -153,6 +152,7 @@
md-selected-item-change="viewer.message.addTag(viewer.tags.selected.name)"
md-search-text="viewer.tags.searchText"
md-items="tag in viewer.service.filterTags(viewer.tags.searchText)"
md-item-text="tag.description"
md-autoselect="true"
label:placeholder="Add a tag">
<md-item-template>
@@ -6,10 +6,13 @@
/**
* @ngInject
*/
MailboxController.$inject = ['$state', '$timeout', '$mdDialog', 'stateAccounts', 'stateAccount', 'stateMailbox', 'encodeUriFilter', 'Dialog', 'Account', 'Mailbox'];
function MailboxController($state, $timeout, $mdDialog, stateAccounts, stateAccount, stateMailbox, encodeUriFilter, Dialog, Account, Mailbox) {
MailboxController.$inject = ['$window', '$state', '$timeout', '$mdDialog', 'stateAccounts', 'stateAccount', 'stateMailbox', 'encodeUriFilter', 'Dialog', 'Account', 'Mailbox'];
function MailboxController($window, $state, $timeout, $mdDialog, stateAccounts, stateAccount, stateMailbox, encodeUriFilter, Dialog, Account, Mailbox) {
var vm = this, messageDialog = null;
// Expose controller
$window.$mailboxController = vm;
Mailbox.selectedFolder = stateMailbox;
vm.service = Mailbox;
+40 -15
View File
@@ -101,10 +101,18 @@
*/
stateAccounts.$inject = ['$q', 'Account'];
function stateAccounts($q, Account) {
var promise = Account.$findAll();
// Fetch list of mailboxes for each account
return promise.then(function(accounts) {
var promises = [];
var accounts, promises = [];
if (window &&
window.opener &&
window.opener.$mailboxController) {
// Mail accounts are available from the parent window
accounts = window.opener.$mailboxController.accounts;
return $q.when(accounts);
}
else {
accounts = Account.$findAll();
// Fetch list of mailboxes for each account
angular.forEach(accounts, function(account, i) {
var mailboxes = account.$getMailboxes();
promises.push(mailboxes.then(function(objects) {
@@ -112,7 +120,7 @@
}));
});
return $q.all(promises);
});
}
}
/**
@@ -160,12 +168,26 @@
/**
* @ngInject
*/
stateMessage.$inject = ['encodeUriFilter', '$stateParams', '$state', 'stateMailbox', 'Message'];
function stateMessage(encodeUriFilter, $stateParams, $state, stateMailbox, Message) {
var data = { uid: $stateParams.messageId.toString() },
message = new Message(stateMailbox.$account.id, stateMailbox, data);
stateMessage.$inject = ['encodeUriFilter', '$q', '$stateParams', '$state', 'stateMailbox', 'Message'];
function stateMessage(encodeUriFilter, $q, $stateParams, $state, stateMailbox, Message) {
var data, message;
return message.$reload();
if (window &&
window.opener &&
window.opener.$messageController &&
window.opener.$messageController.message.uid == parseInt($stateParams.messageId)) {
// Message is available from the parent window
message = new Message(stateMailbox.$account.id,
stateMailbox,
window.opener.$messageController.message.$omit());
return $q.when(message);
}
else {
// Message is not available; load it from the server
data = { uid: $stateParams.messageId.toString() };
message = new Message(stateMailbox.$account.id, stateMailbox, data);
return message.$reload();
}
}
/**
@@ -179,10 +201,14 @@
/**
* @ngInject
*/
runBlock.$inject = ['$rootScope'];
function runBlock($rootScope) {
runBlock.$inject = ['$window', '$rootScope', '$log'];
function runBlock($window, $rootScope, $log) {
$rootScope.$on('$stateChangeError', function(event, toState, toParams, fromState, fromParams, error) {
$log.error(error);
$window.close();
});
$rootScope.$on('$routeChangeError', function(event, current, previous, rejection) {
console.error(event, current, previous, rejection);
$log.error(event, current, previous, rejection);
});
}
@@ -207,8 +233,7 @@
}
})
.finally(function() {
if ($window.opener)
$window.close();
$window.close();
});
}
@@ -22,13 +22,11 @@
// Data is immediately available
if (typeof futureMessageData.then !== 'function') {
//console.debug(JSON.stringify(futureMessageData, undefined, 2));
if (angular.isDefined(lazy) && lazy) {
this.uid = futureMessageData.uid;
}
else {
if (angular.isUndefined(lazy) || !lazy) {
angular.extend(this, futureMessageData);
this.$formatFullAddresses();
}
this.uid = parseInt(futureMessageData.uid);
}
else {
// The promise will be unwrapped first
@@ -132,7 +130,7 @@
var oldUID = (this.uid || -1);
if (oldUID != parseInt(uid)) {
this.uid = uid;
this.uid = parseInt(uid);
if (oldUID > -1) {
oldUID = oldUID.toString();
if (angular.isDefined(this.$mailbox.uidsMap[oldUID])) {
@@ -142,7 +140,8 @@
}
else {
// Refresh selected folder if it's the drafts mailbox
if (this.$mailbox.constructor.selectedFolder.type == 'draft') {
if (this.$mailbox.constructor.selectedFolder &&
this.$mailbox.constructor.selectedFolder.type == 'draft') {
this.$mailbox.constructor.selectedFolder.$filter();
}
}
@@ -646,13 +645,6 @@
}
});
// Format addresses as arrays
_.each(['from', 'to', 'cc', 'bcc', 'reply-to'], function(type) {
if (message[type])
message[type] = _.invoke(message[type].split(','), 'trim');
});
//Message.$log.debug(JSON.stringify(message, undefined, 2));
return message;
};
@@ -10,6 +10,10 @@
function MessageController($window, $scope, $state, $mdDialog, stateAccounts, stateAccount, stateMailbox, stateMessage, encodeUriFilter, sgSettings, focus, Dialog, Account, Mailbox, Message) {
var vm = this, messageDialog = null, popupWindow = null;
// Expose controller
$window.$messageController = vm;
vm.$state = $state;
vm.accounts = stateAccounts;
vm.account = stateAccount;
vm.mailbox = stateMailbox;
@@ -32,14 +36,54 @@
vm.toggleRawSource = toggleRawSource;
vm.showRawSource = false;
// Watch the message model "flags" attribute to remove on-the-fly a tag from the IMAP message
// when removed from the message viewer.
// TODO: this approach should be reviewed once md-chips supports ng-change.
$scope.$watchCollection('viewer.message.flags', function(oldTags, newTags) {
_.each(_.difference(newTags, oldTags), function(tag) {
vm.message.removeTag(tag);
// One-way refresh of the parent window when modifying the message from a popup window.
if ($window.opener) {
// Update the message flags. The message must be displayed in the parent window.
$scope.$watchCollection('viewer.message.flags', function(newTags, oldTags) {
var ctrls;
if (newTags || oldTags) {
ctrls = $parentControllers();
if (ctrls.messageCtrl) {
ctrls.messageCtrl.service.$timeout(function() {
ctrls.messageCtrl.message.flags = newTags;
});
}
}
});
});
// Update the "isflagged" (star icon) of the message. The mailbox must be displayed in the parent window.
$scope.$watch('viewer.message.isflagged', function(isflagged, wasflagged) {
var ctrls = $parentControllers();
if (ctrls.mailboxCtrl) {
ctrls.mailboxCtrl.service.$timeout(function() {
var message = _.find(ctrls.mailboxCtrl.selectedFolder.$messages, { uid: vm.message.uid });
message.isflagged = isflagged;
});
}
});
}
/**
* If this is a popup window, retrieve the matching controllers (mailbox and message) of the parent window.
*/
function $parentControllers() {
var message, mailbox, ctrls = {};
if ($window.opener) {
// Deleting the message from a popup window
if ($window.opener.$mailboxController &&
$window.opener.$mailboxController.selectedFolder.$id() == stateMailbox.$id()) {
// The message mailbox is opened in the parent window
mailbox = $window.opener.$mailboxController;
ctrls.mailboxCtrl = mailbox;
if ($window.opener.$messageController &&
$window.opener.$messageController.message.uid == stateMessage.uid) {
// The message is opened in the parent window
message = $window.opener.$messageController;
ctrls.messageCtrl = message;
}
}
}
return ctrls;
}
function showDetailedRecipients($event) {
vm.$showDetailedRecipients = true;
@@ -48,16 +92,36 @@
}
function doDelete() {
stateMailbox.$deleteMessages([stateMessage]).then(function() {
var parentCtrls = $parentControllers(), mailbox, message, state;
if (parentCtrls.messageCtrl) {
mailbox = parentCtrls.mailboxCtrl.selectedFolder;
message = parentCtrls.messageCtrl.message;
state = parentCtrls.messageCtrl.$state;
}
else {
mailbox = stateMailbox;
message = stateMessage;
state = $state;
}
mailbox.$deleteMessages([message]).then(function() {
// Remove message from list of messages
var index = _.findIndex(stateMailbox.$messages, function(o) {
return o.uid == stateMessage.uid;
var index = _.findIndex(mailbox.$messages, function(o) {
return o.uid == message.uid;
});
if (index != -1)
stateMailbox.$messages.splice(index, 1);
mailbox.$messages.splice(index, 1);
// Remove message object from scope
vm.message = null;
$state.go('mail.account.mailbox', { accountId: stateAccount.id, mailboxId: encodeUriFilter(stateMailbox.path) });
message = null;
// Navigate to mailbox if not in popup window
if (angular.isDefined(state)) {
try {
state.go('mail.account.mailbox', { mailboxId: encodeUriFilter(mailbox.path) });
}
catch (error) {}
}
closePopup();
});
}
@@ -77,18 +141,20 @@
controllerAs: 'editor',
locals: {
stateAccounts: vm.accounts,
stateAccount: vm.account,
stateMessage: message,
stateRecipients: recipients
}
})
.finally(function() {
messageDialog = null;
closePopup();
});
}
}
function close() {
$state.go('mail.account.mailbox', { accountId: stateAccount.id, mailboxId: encodeUriFilter(stateMailbox.path) }).then(function() {
$state.go('mail.account.mailbox').then(function() {
vm.message = null;
delete stateMailbox.selectedMessage;
});
@@ -139,7 +205,8 @@
}
function closePopup() {
$window.close();
if ($window.opener)
$window.close();
}
function newMessage($event, recipient) {
@@ -6,8 +6,8 @@
/**
* @ngInject
*/
MessageEditorController.$inject = ['$stateParams', '$mdConstant', '$mdDialog', '$mdToast', 'FileUploader', 'stateAccounts', 'stateMessage', 'stateRecipients', '$timeout', 'Dialog', 'AddressBook', 'Card', 'Preferences'];
function MessageEditorController($stateParams, $mdConstant, $mdDialog, $mdToast, FileUploader, stateAccounts, stateMessage, stateRecipients, $timeout, Dialog, AddressBook, Card, Preferences) {
MessageEditorController.$inject = ['$window', '$stateParams', '$mdConstant', '$mdDialog', '$mdToast', 'FileUploader', 'stateAccounts', 'stateAccount', 'stateMessage', 'stateRecipients', 'encodeUriFilter', '$timeout', 'Dialog', 'AddressBook', 'Card', 'Preferences'];
function MessageEditorController($window, $stateParams, $mdConstant, $mdDialog, $mdToast, FileUploader, stateAccounts, stateAccount, stateMessage, stateRecipients, encodeUriFilter, $timeout, Dialog, AddressBook, Card, Preferences) {
var vm = this, semicolon = 186;
vm.addRecipient = addRecipient;
@@ -77,6 +77,33 @@
vm.message.editable.to = _.union(vm.message.editable.to, _.pluck(stateRecipients, 'full'));
}
/**
* If this is a popup window, retrieve the mailbox controller of the parent window.
*/
function $parentControllers() {
var originMessage, ctrls = {};
if ($window.opener) {
if ($window.opener.$mailboxController) {
if ($window.opener.$mailboxController.selectedFolder.type == 'draft') {
ctrls.draftMailboxCtrl = $window.opener.$mailboxController;
if ($window.opener.$messageController &&
$window.opener.$messageController.message.uid == stateMessage.uid) {
// The draft is opened in the parent window
ctrls.draftMessageCtrl = $window.opener.$messageController;
}
}
else if (stateMessage.origin) {
originMessage = stateMessage.origin.message;
if ($window.opener.$mailboxController.selectedFolder.$id() == originMessage.$mailbox.$id()) {
// The message mailbox is opened in the parent window
ctrls.originMailboxCtrl = $window.opener.$mailboxController;
}
}
}
}
return ctrls;
}
function addAttachments() {
// Add existing attached files to uploader
var i, data, fileItem;
@@ -114,7 +141,18 @@
}
function save() {
var ctrls = $parentControllers();
vm.message.$save().then(function(data) {
if (ctrls.draftMailboxCtrl) {
// We're saving a draft from a popup window.
// Reload draft mailbox
ctrls.draftMailboxCtrl.selectedFolder.$filter().then(function() {
if (ctrls.draftMessageCtrl) {
// Reload selected message
ctrls.draftMessageCtrl.$state.go('mail.account.mailbox.message', { messageId: vm.message.uid });
}
});
}
$mdToast.show(
$mdToast.simple()
.content(l('Your email has been saved'))
@@ -124,10 +162,26 @@
}
function send() {
var ctrls = $parentControllers();
if (vm.autosave)
$timeout.cancel(vm.autosave);
vm.message.$send().then(function(data) {
if (ctrls.draftMailboxCtrl) {
// We're sending a draft from a popup window and the draft mailbox is opened.
// Reload draft mailbox
ctrls.draftMailboxCtrl.selectedFolder.$filter().then(function() {
if (ctrls.draftMessageCtrl) {
// Close draft
ctrls.draftMessageCtrl.close();
}
});
}
if (ctrls.originMailboxCtrl) {
// We're sending a draft from a popup window and the original mailbox is opened.
// Reload mailbox
ctrls.originMailboxCtrl.selectedFolder.$filter();
}
$mdToast.show(
$mdToast.simple()
.content(l('Your email has been sent'))