perf(mail(js)): various optimizations
We now use IMAP QRESYNC to synchronize mailbox.pull/299/head
parent
b4ccafd546
commit
a9c6f09273
|
@ -105,7 +105,14 @@
|
|||
matchingSyncToken: (NSString *) theSyncToken
|
||||
fromDate: (NSCalendarDate *) theStartDate
|
||||
initialLoad: (BOOL) initialLoadInProgress;
|
||||
/* flags */
|
||||
- (NSArray *) syncTokenFieldsWithProperties: (NSDictionary *) theProperties
|
||||
matchingSyncToken: (NSString *) theSyncToken
|
||||
fromDate: (NSCalendarDate *) theStartDate
|
||||
initialLoad: (BOOL) initialLoadInProgress
|
||||
sortOrdering: (id) theSortOrdering
|
||||
threaded: (BOOL) isThreaded;
|
||||
|
||||
/* flags */
|
||||
|
||||
- (NSException *) addFlagsToAllMessages: (id) _f;
|
||||
|
||||
|
|
|
@ -2259,6 +2259,21 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
|||
matchingSyncToken: (NSString *) theSyncToken
|
||||
fromDate: (NSCalendarDate *) theStartDate
|
||||
initialLoad: (BOOL) initialLoadInProgress
|
||||
{
|
||||
return [self syncTokenFieldsWithProperties: theProperties
|
||||
matchingSyncToken: theSyncToken
|
||||
fromDate: theStartDate
|
||||
initialLoad: initialLoadInProgress
|
||||
sortOrdering: nil
|
||||
threaded: NO];
|
||||
}
|
||||
|
||||
- (NSArray *) syncTokenFieldsWithProperties: (NSDictionary *) theProperties
|
||||
matchingSyncToken: (NSString *) theSyncToken
|
||||
fromDate: (NSCalendarDate *) theStartDate
|
||||
initialLoad: (BOOL) initialLoadInProgress
|
||||
sortOrdering: (id) theSortOrdering
|
||||
threaded: (BOOL) isThreaded
|
||||
{
|
||||
EOQualifier *searchQualifier;
|
||||
NSMutableArray *allTokens;
|
||||
|
@ -2316,17 +2331,19 @@ _compareFetchResultsByMODSEQ (id entry1, id entry2, void *data)
|
|||
|
||||
// we fetch modified or added uids
|
||||
uids = [self fetchUIDsMatchingQualifier: searchQualifier
|
||||
sortOrdering: nil];
|
||||
sortOrdering: theSortOrdering];
|
||||
|
||||
fetchResults = [(NSDictionary *)[self fetchUIDs: uids
|
||||
parts: [NSArray arrayWithObjects: @"modseq", @"flags", nil]]
|
||||
objectForKey: @"fetch"];
|
||||
|
||||
/* NOTE: we sort items manually because Cyrus does not properly sort
|
||||
entries with a MODSEQ of 0 */
|
||||
fetchResults
|
||||
= [fetchResults sortedArrayUsingFunction: _compareFetchResultsByMODSEQ
|
||||
context: NULL];
|
||||
if (theSortOrdering == nil)
|
||||
{
|
||||
/* NOTE: we sort items manually because Cyrus does not properly sort
|
||||
entries with a MODSEQ of 0 */
|
||||
fetchResults = [fetchResults sortedArrayUsingFunction: _compareFetchResultsByMODSEQ
|
||||
context: NULL];
|
||||
}
|
||||
|
||||
for (i = 0; i < [fetchResults count]; i++)
|
||||
{
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#import <NGExtensions/NSNull+misc.h>
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGImap4/NGImap4Connection.h>
|
||||
#import <NGImap4/NGImap4Envelope.h>
|
||||
|
||||
#import <EOControl/EOQualifier.h>
|
||||
|
@ -489,7 +490,7 @@
|
|||
}
|
||||
else
|
||||
fetchQualifier = notDeleted;
|
||||
|
||||
|
||||
sortedUIDs = [mailFolder fetchUIDsMatchingQualifier: fetchQualifier
|
||||
sortOrdering: [self imap4SortOrdering]
|
||||
threaded: sortByThread];
|
||||
|
@ -648,7 +649,7 @@
|
|||
int count;
|
||||
|
||||
data = [NSMutableDictionary dictionary];
|
||||
|
||||
|
||||
// TODO: we might want to flush the caches?
|
||||
//[folder flushMailCaches];
|
||||
[folder expungeLastMarkedFolder];
|
||||
|
@ -661,6 +662,16 @@
|
|||
return nil;
|
||||
}
|
||||
|
||||
// We first make sure QRESYNC is enabled
|
||||
if (![[folder imap4Connection] enableExtensions: [NSArray arrayWithObject: @"QRESYNC"]])
|
||||
{
|
||||
NSString *tag = [folder davCollectionTag];
|
||||
if (![tag isEqualToString: @"-1"])
|
||||
{
|
||||
[data setObject: tag forKey: @"syncToken"];
|
||||
}
|
||||
}
|
||||
|
||||
// Get rid of the extra parenthesis
|
||||
// uids = [[[[uids stringValue] stringByReplacingOccurrencesOfString:@"(" withString:@""] stringByReplacingOccurrencesOfString:@")" withString:@""] componentsSeparatedByString:@","];
|
||||
|
||||
|
@ -786,6 +797,72 @@
|
|||
return response;
|
||||
}
|
||||
|
||||
- (id <WOActionResults>) getChangesAction
|
||||
{
|
||||
NSArray *changedMessages, *headers;
|
||||
NSDictionary *requestContent, *data, *changedMessage;
|
||||
NSMutableArray *changedUids, *deletedUids;
|
||||
NSString *syncToken, *newSyncToken, *uid;
|
||||
SOGoMailFolder *folder;
|
||||
WORequest *request;
|
||||
WOResponse *response;
|
||||
int i, max;
|
||||
|
||||
request = [context request];
|
||||
requestContent = [[request contentAsString] objectFromJSONString];
|
||||
response = nil;
|
||||
folder = [self clientObject];
|
||||
syncToken = [requestContent objectForKey: @"syncToken"];
|
||||
newSyncToken = [folder davCollectionTag];
|
||||
|
||||
if ([syncToken length] && ![syncToken isEqual: newSyncToken])
|
||||
{
|
||||
// Fetch list of changed uids
|
||||
changedMessages = [folder syncTokenFieldsWithProperties: nil
|
||||
matchingSyncToken: syncToken
|
||||
fromDate: nil
|
||||
initialLoad: NO
|
||||
sortOrdering: [self imap4SortOrdering]
|
||||
threaded: sortByThread];
|
||||
if ((max = [changedMessages count]))
|
||||
{
|
||||
// Split new or modified uids from deleted uids
|
||||
changedUids = [NSMutableArray array];
|
||||
deletedUids = [NSMutableArray array];
|
||||
for (i = 0; i < max; i++)
|
||||
{
|
||||
changedMessage = [changedMessages objectAtIndex: i];
|
||||
uid = [[changedMessage allKeys] lastObject];
|
||||
if ([[changedMessage objectForKey: uid] isEqual: [NSNull null]])
|
||||
[deletedUids addObject: uid];
|
||||
else
|
||||
[changedUids addObject: uid];
|
||||
}
|
||||
|
||||
// Fetch headers for new or modified messages
|
||||
headers = [self getHeadersForUIDs: changedUids
|
||||
inFolder: folder];
|
||||
|
||||
data = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
changedUids, @"changed",
|
||||
deletedUids, @"deleted",
|
||||
headers, @"headers",
|
||||
newSyncToken, @"syncToken",
|
||||
nil];
|
||||
response = [self responseWithStatus: 200 andJSONRepresentation: data];
|
||||
}
|
||||
}
|
||||
if (!response)
|
||||
{
|
||||
data = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
newSyncToken, @"syncToken",
|
||||
nil];
|
||||
response = [self responseWithStatus: 200 andJSONRepresentation: data];
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
- (NSArray *) getHeadersForUIDs: (NSArray *) uids
|
||||
inFolder: (SOGoMailFolder *) mailFolder
|
||||
{
|
||||
|
|
|
@ -19,6 +19,11 @@
|
|||
actionClass = "UIxMailListActions";
|
||||
actionName = "getUIDs";
|
||||
};
|
||||
changes = {
|
||||
protectedBy = "View";
|
||||
actionClass = "UIxMailListActions";
|
||||
actionName = "getChanges";
|
||||
};
|
||||
headers = {
|
||||
protectedBy = "View";
|
||||
actionClass = "UIxMailListActions";
|
||||
|
|
|
@ -156,7 +156,7 @@
|
|||
<md-button class="sg-icon-button" ng-click="mailbox.unselectMessages()">
|
||||
<md-icon>arrow_back</md-icon>
|
||||
</md-button>
|
||||
<label class="md-truncate"><span ng-bind="mailbox.service.selectedFolder.$selectedCount()"><!-- count --></span> <var:string label:value="selected"/></label>
|
||||
<label class="md-truncate"><span ng-bind="mailbox.service.selectedFolder.selectedCount()"><!-- count --></span> <var:string label:value="selected"/></label>
|
||||
<div class="md-flex"><!-- spacer --></div>
|
||||
<md-button class="sg-icon-button" ng-click="mailbox.selectAll()">
|
||||
<md-tooltip md-direction="bottom"><var:string label:value="Select All"/></md-tooltip>
|
||||
|
@ -286,8 +286,8 @@
|
|||
<md-list class="sg-section-list"
|
||||
ng-class="{ 'sg-list-selectable': mailbox.mode.multiple }"
|
||||
sg-draggable="mailbox.selectedFolder"
|
||||
sg-drag-start="mailbox.selectedFolder.hasSelectedMessage() || mailbox.selectedFolder.$selectedCount()"
|
||||
sg-drag-count="mailbox.selectedFolder.$selectedCount()">
|
||||
sg-drag-start="mailbox.selectedFolder.hasSelectedMessage() || mailbox.selectedFolder.selectedCount()"
|
||||
sg-drag-count="mailbox.selectedFolder.selectedCount()">
|
||||
<md-list-item
|
||||
aria-label="{{currentMessage.subject}}"
|
||||
class="sg-message-list-item"
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
$Preferences: Preferences,
|
||||
$query: { sort: 'arrival', asc: 0 }, // The default sort must match [UIxMailListActions defaultSortKey]
|
||||
selectedFolder: null,
|
||||
$selectedMessages: [],
|
||||
$refreshTimeout: null,
|
||||
$virtualMode: false,
|
||||
$virtualPath: false,
|
||||
|
@ -265,9 +266,8 @@
|
|||
if (index >= 0 && index < visibleMessages.length) {
|
||||
message = visibleMessages[index];
|
||||
this.$lastVisibleIndex = Math.max(0, index - 3); // Magic number is NUM_EXTRA from virtual-repeater.js
|
||||
|
||||
if (this.$loadMessage(message.uid))
|
||||
return message;
|
||||
this.$loadMessage(message.uid);
|
||||
return message; // skeleton is displayed while headers are being fetched
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
@ -283,23 +283,25 @@
|
|||
};
|
||||
|
||||
/**
|
||||
* @function $selectedMessages
|
||||
* @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; });
|
||||
Mailbox.prototype.selectedMessages = function(options) {
|
||||
if (options && options.updateCache)
|
||||
this.$selectedMessages = _.filter(this.$messages, function(message) { return message.selected; });
|
||||
return this.$selectedMessages;
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $selectedCount
|
||||
* @function selectedCount
|
||||
* @memberof Mailbox.prototype
|
||||
* @desc Return the number of messages selected by the user.
|
||||
* @returns the number of selected messages
|
||||
*/
|
||||
Mailbox.prototype.$selectedCount = function() {
|
||||
return this.$selectedMessages().length;
|
||||
Mailbox.prototype.selectedCount = function() {
|
||||
return this.$selectedMessages.length;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -308,9 +310,10 @@
|
|||
* @desc Unselect all messages.
|
||||
*/
|
||||
Mailbox.prototype.$unselectMessages = function() {
|
||||
_.forEach(this.$selectedMessages(), function(message) {
|
||||
_.forEach(this.$selectedMessages, function(message) {
|
||||
message.selected = false;
|
||||
});
|
||||
this.$selectedMessages = [];
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -321,7 +324,7 @@
|
|||
* @returns true if the specified message is displayed
|
||||
*/
|
||||
Mailbox.prototype.isSelectedMessage = function(messageId) {
|
||||
return this.selectedMessage == messageId;
|
||||
return this.$selectedMessage == messageId;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -330,10 +333,9 @@
|
|||
* @desc Return the currently visible message.
|
||||
* @returns a Message instance or undefined if no message is displayed
|
||||
*/
|
||||
Mailbox.prototype.$selectedMessage = function() {
|
||||
Mailbox.prototype.selectedMessage = function() {
|
||||
var _this = this;
|
||||
|
||||
return _.find(this.$messages, function(message) { return message.uid == _this.selectedMessage; });
|
||||
return _.find(this.$messages, function(message) { return message.uid == _this.$selectedMessage; });
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -343,7 +345,7 @@
|
|||
* @returns a number or undefined if no message is selected
|
||||
*/
|
||||
Mailbox.prototype.$selectedMessageIndex = function() {
|
||||
return this.uidsMap[this.selectedMessage];
|
||||
return this.uidsMap[this.$selectedMessage];
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -353,7 +355,7 @@
|
|||
* @returns true if the a message is selected
|
||||
*/
|
||||
Mailbox.prototype.hasSelectedMessage = function() {
|
||||
return angular.isDefined(this.selectedMessage);
|
||||
return angular.isDefined(this.$selectedMessage);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -371,7 +373,7 @@
|
|||
* @returns a promise of the HTTP operation
|
||||
*/
|
||||
Mailbox.prototype.$filter = function(sortingAttributes, filters) {
|
||||
var _this = this, options = {};
|
||||
var _this = this, action = 'view', options = {};
|
||||
|
||||
if (!angular.isDefined(this.unseenCount))
|
||||
this.unseenCount = 0;
|
||||
|
@ -403,6 +405,10 @@
|
|||
}
|
||||
});
|
||||
}
|
||||
else if (!sortingAttributes && this.$syncToken) {
|
||||
action = 'changes';
|
||||
options.syncToken = this.$syncToken;
|
||||
}
|
||||
|
||||
// Restart the refresh timer, if needed
|
||||
if (!Mailbox.$virtualMode) {
|
||||
|
@ -413,14 +419,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
var futureMailboxData = Mailbox.$$resource.post(this.id, 'view', options);
|
||||
var futureMailboxData = Mailbox.$$resource.post(this.id, action, options);
|
||||
return this.$unwrap(futureMailboxData);
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $loadMessage
|
||||
* @memberof Mailbox.prototype
|
||||
* @desc Check if the message is loaded and in any case, fetch more messages headers from the server.
|
||||
* @desc Check if the message headers are loaded and in any case, fetch more messages headers from the server.
|
||||
* @returns true if the message metadata are already fetched
|
||||
*/
|
||||
Mailbox.prototype.$loadMessage = function(messageId) {
|
||||
|
@ -765,8 +771,8 @@
|
|||
if (selectedIndex > -1) {
|
||||
uids.splice(selectedIndex, 1);
|
||||
delete _this.uidsMap[message.uid];
|
||||
if (message.uid == _this.selectedMessage)
|
||||
delete _this.selectedMessage;
|
||||
if (message.uid == _this.$selectedMessage)
|
||||
delete _this.$selectedMessage;
|
||||
_this.$messages.splice(index, 1);
|
||||
if (index < firstIndex)
|
||||
firstIndex = index;
|
||||
|
@ -813,7 +819,10 @@
|
|||
});
|
||||
}
|
||||
|
||||
return _deleteMessages(0, Math.min(batchSize, uids.length));
|
||||
return _deleteMessages(0, Math.min(batchSize, uids.length)).then(function(firstIndex) {
|
||||
_this.$selectedMessages = []; // reset selection
|
||||
return firstIndex;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -860,6 +869,7 @@
|
|||
uids = _.map(messages, 'uid');
|
||||
return Mailbox.$$resource.post(this.id, 'moveMessages', {uids: uids, folder: folder})
|
||||
.then(function() {
|
||||
_this.$selectedMessages = []; // reset selection
|
||||
return _this.$_deleteMessages(uids, messages);
|
||||
});
|
||||
};
|
||||
|
@ -959,12 +969,48 @@
|
|||
|
||||
this.$futureMailboxData = futureMailboxData;
|
||||
this.$futureMailboxData.then(function(data) {
|
||||
var selectedMessages = _.map(_this.$selectedMessages(), 'uid');
|
||||
var selectedMessages = _.map(_this.$selectedMessages, 'uid');
|
||||
Mailbox.$timeout(function() {
|
||||
var uids, headers, headersFields;
|
||||
var uids, headers, headersFields, msgObject;
|
||||
|
||||
if (!data.uids || _this.$topIndex > data.uids.length - 1)
|
||||
_this.$topIndex = 0;
|
||||
if (data.syncToken)
|
||||
_this.$syncToken = data.syncToken;
|
||||
|
||||
if (data.deleted) {
|
||||
var deletedMessages = [];
|
||||
_.forEachRight(data.deleted, function(uid, i) {
|
||||
var j = _this.uidsMap[uid.toString()];
|
||||
if (j >= 0 && _this.$messages[j])
|
||||
deletedMessages.push(_this.$messages[j]);
|
||||
else
|
||||
// Unkown message
|
||||
data.deleted.splice(i, 1);
|
||||
});
|
||||
if (deletedMessages.length)
|
||||
_this.$_deleteMessages(data.deleted, deletedMessages);
|
||||
}
|
||||
if (data.changed) {
|
||||
var i = 0, j;
|
||||
_.forEach(data.changed, function(uid) {
|
||||
if (angular.isUndefined(_this.uidsMap[uid.toString()])) {
|
||||
// New messsage; update map of UID <=> index
|
||||
_this.uidsMap[uid] = i;
|
||||
msgObject = new Mailbox.$Message(_this.$account.id, _this, {uid: uid}, true);
|
||||
_this.$messages.splice(i, 0, msgObject);
|
||||
i++;
|
||||
}
|
||||
});
|
||||
|
||||
if (i > 0) {
|
||||
// New messages received, update uidsMap
|
||||
for (j = i; j < _this.$messages.length; j++) {
|
||||
msgObject = _this.$messages[j];
|
||||
_this.uidsMap[msgObject.uid] += i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (data.uids) {
|
||||
// Initialization phase, we received complete list of UIDs
|
||||
|
|
|
@ -166,9 +166,9 @@
|
|||
this.cancelSearch = function() {
|
||||
vm.mode.search = false;
|
||||
vm.selectedFolder.$filter().then(function() {
|
||||
if (vm.selectedFolder.selectedMessage) {
|
||||
if (vm.selectedFolder.$selectedMessage) {
|
||||
$timeout(function() {
|
||||
vm.selectedFolder.$topIndex = vm.selectedFolder.uidsMap[vm.selectedFolder.selectedMessage];
|
||||
vm.selectedFolder.$topIndex = vm.selectedFolder.uidsMap[vm.selectedFolder.$selectedMessage];
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -320,14 +320,14 @@
|
|||
selectedIndex, nextSelectedIndex, i;
|
||||
|
||||
if (!message)
|
||||
message = folder.$selectedMessage();
|
||||
message = folder.selectedMessage();
|
||||
if (!message)
|
||||
return true;
|
||||
|
||||
message.selected = !message.selected;
|
||||
vm.mode.multiple += message.selected? 1 : -1;
|
||||
|
||||
// Select closest range of messages when shift key is pressed
|
||||
if ($event.shiftKey && folder.$selectedCount() > 1) {
|
||||
if ($event.shiftKey && folder.selectedCount() > 0) {
|
||||
selectedIndex = folder.uidsMap[message.uid];
|
||||
// Search for next selected message above
|
||||
nextSelectedIndex = selectedIndex - 2;
|
||||
|
@ -349,6 +349,8 @@
|
|||
}
|
||||
}
|
||||
|
||||
folder.selectedMessages({ updateCache: true });
|
||||
vm.mode.multiple = vm.selectedFolder.selectedCount();
|
||||
$event.preventDefault();
|
||||
$event.stopPropagation();
|
||||
};
|
||||
|
@ -368,7 +370,7 @@
|
|||
// This function must not be called in virtual mode.
|
||||
function _unselectMessage(message, index) {
|
||||
var nextMessage, previousMessage, nextIndex = index;
|
||||
vm.mode.multiple = vm.selectedFolder.$selectedCount();
|
||||
vm.mode.multiple = vm.selectedFolder.selectedCount();
|
||||
if (message) {
|
||||
// Select either the next or previous message
|
||||
if (index > 0) {
|
||||
|
@ -404,7 +406,7 @@
|
|||
}
|
||||
|
||||
this.confirmDeleteSelectedMessages = function($event) {
|
||||
var selectedMessages = vm.selectedFolder.$selectedMessages();
|
||||
var selectedMessages = vm.selectedFolder.selectedMessages();
|
||||
|
||||
if (vm.messageDialog === null && _.size(selectedMessages) > 0)
|
||||
vm.messageDialog = Dialog.confirm(l('Confirmation'),
|
||||
|
@ -456,9 +458,10 @@
|
|||
|
||||
this.markOrUnMarkMessagesAsJunk = function() {
|
||||
var moveSelectedMessage = vm.selectedFolder.hasSelectedMessage();
|
||||
var selectedMessages = vm.selectedFolder.$selectedMessages();
|
||||
var selectedMessages = vm.selectedFolder.selectedMessages();
|
||||
if (_.size(selectedMessages) === 0 && moveSelectedMessage)
|
||||
selectedMessages = [vm.selectedFolder.$selectedMessage()];
|
||||
// No selection, user has pressed keyboard shortcut
|
||||
selectedMessages = [vm.selectedFolder.selectedMessage()];
|
||||
if (_.size(selectedMessages) > 0)
|
||||
vm.selectedFolder.$markOrUnMarkMessagesAsJunk(selectedMessages).then(function() {
|
||||
var dstFolder = '/' + vm.account.id + '/folderINBOX';
|
||||
|
@ -481,12 +484,12 @@
|
|||
};
|
||||
|
||||
this.copySelectedMessages = function(dstFolder) {
|
||||
var selectedMessages = vm.selectedFolder.$selectedMessages();
|
||||
var selectedMessages = vm.selectedFolder.selectedMessages();
|
||||
if (_.size(selectedMessages) > 0)
|
||||
vm.selectedFolder.$copyMessages(selectedMessages, '/' + dstFolder).then(function() {
|
||||
$mdToast.show(
|
||||
$mdToast.simple()
|
||||
.textContent(l('%{0} message(s) copied', vm.selectedFolder.$selectedCount()))
|
||||
.textContent(l('%{0} message(s) copied', vm.selectedFolder.selectedCount()))
|
||||
.position('top right')
|
||||
.hideDelay(2000));
|
||||
});
|
||||
|
@ -494,8 +497,8 @@
|
|||
|
||||
this.moveSelectedMessages = function(dstFolder) {
|
||||
var moveSelectedMessage = vm.selectedFolder.hasSelectedMessage();
|
||||
var selectedMessages = vm.selectedFolder.$selectedMessages();
|
||||
var count = vm.selectedFolder.$selectedCount();
|
||||
var selectedMessages = vm.selectedFolder.selectedMessages();
|
||||
var count = vm.selectedFolder.selectedCount();
|
||||
if (_.size(selectedMessages) > 0)
|
||||
vm.selectedFolder.$moveMessages(selectedMessages, '/' + dstFolder).then(function(index) {
|
||||
$mdToast.show(
|
||||
|
@ -520,8 +523,11 @@
|
|||
var count = 0;
|
||||
_.forEach(_currentMailboxes(), function(folder) {
|
||||
var i = 0, length = folder.$messages.length;
|
||||
for (; i < length; i++)
|
||||
folder.$selectedMessages = [];
|
||||
for (; i < length; i++) {
|
||||
folder.$messages[i].selected = true;
|
||||
folder.$selectedMessages.push(folder.$messages[i]);
|
||||
}
|
||||
count += length;
|
||||
});
|
||||
vm.mode.multiple = count;
|
||||
|
@ -529,6 +535,7 @@
|
|||
|
||||
this.unselectMessages = function() {
|
||||
_.forEach(_currentMailboxes(), function(folder) {
|
||||
folder.$selectedMessages = [];
|
||||
_.forEach(folder.$messages, function(message) {
|
||||
message.selected = false;
|
||||
});
|
||||
|
@ -537,7 +544,7 @@
|
|||
};
|
||||
|
||||
this.markSelectedMessagesAsFlagged = function() {
|
||||
var selectedMessages = vm.selectedFolder.$selectedMessages();
|
||||
var selectedMessages = vm.selectedFolder.selectedMessages();
|
||||
if (_.size(selectedMessages) > 0)
|
||||
vm.selectedFolder.$flagMessages(selectedMessages, '\\Flagged', 'add').then(function(messages) {
|
||||
_.forEach(messages, function(message) {
|
||||
|
@ -547,7 +554,7 @@
|
|||
};
|
||||
|
||||
this.markSelectedMessagesAsUnread = function() {
|
||||
var selectedMessages = vm.selectedFolder.$selectedMessages();
|
||||
var selectedMessages = vm.selectedFolder.selectedMessages();
|
||||
if (_.size(selectedMessages) > 0) {
|
||||
vm.selectedFolder.$flagMessages(selectedMessages, 'seen', 'remove').then(function(messages) {
|
||||
_.forEach(messages, function(message) {
|
||||
|
@ -560,7 +567,7 @@
|
|||
};
|
||||
|
||||
this.markSelectedMessagesAsRead = function() {
|
||||
var selectedMessages = vm.selectedFolder.$selectedMessages();
|
||||
var selectedMessages = vm.selectedFolder.selectedMessages();
|
||||
if (_.size(selectedMessages) > 0) {
|
||||
vm.selectedFolder.$flagMessages(selectedMessages, 'seen', 'add').then(function(messages) {
|
||||
_.forEach(messages, function(message) {
|
||||
|
|
|
@ -321,11 +321,11 @@
|
|||
var dstId, messages, uids, clearMessageView, promise, success;
|
||||
|
||||
dstId = '/' + dstFolder.id;
|
||||
messages = srcFolder.$selectedMessages();
|
||||
messages = srcFolder.selectedMessages();
|
||||
if (messages.length === 0)
|
||||
messages = [srcFolder.$selectedMessage()];
|
||||
messages = [srcFolder.selectedMessage()];
|
||||
uids = _.map(messages, 'uid');
|
||||
clearMessageView = (srcFolder.selectedMessage && uids.indexOf(srcFolder.selectedMessage) >= 0);
|
||||
clearMessageView = (srcFolder.$selectedMessage && uids.indexOf(srcFolder.$selectedMessage) >= 0);
|
||||
|
||||
if (mode == 'copy') {
|
||||
promise = srcFolder.$copyMessages(messages, dstId);
|
||||
|
|
|
@ -317,7 +317,7 @@
|
|||
return messageObject.uid == parseInt($stateParams.messageId);
|
||||
});
|
||||
|
||||
if (message) {
|
||||
if (message && message.$reload) {
|
||||
return message.$reload({useCache: true});
|
||||
}
|
||||
else {
|
||||
|
@ -331,7 +331,7 @@
|
|||
*/
|
||||
onEnterMessage.$inject = ['$stateParams', 'stateMailbox'];
|
||||
function onEnterMessage($stateParams, stateMailbox) {
|
||||
stateMailbox.selectedMessage = parseInt($stateParams.messageId);
|
||||
stateMailbox.$selectedMessage = parseInt($stateParams.messageId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -339,7 +339,7 @@
|
|||
*/
|
||||
onExitMessage.$inject = ['stateMailbox'];
|
||||
function onExitMessage(stateMailbox) {
|
||||
delete stateMailbox.selectedMessage;
|
||||
delete stateMailbox.$selectedMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,8 +26,10 @@
|
|||
this.init(futureMessageData);
|
||||
}
|
||||
this.uid = parseInt(futureMessageData.uid);
|
||||
this.selected = !!futureMessageData.selected;
|
||||
this.level = parseInt(futureMessageData.level);
|
||||
this.first = parseInt(futureMessageData.first) === 1;
|
||||
this.flags = [];
|
||||
if (this.first) {
|
||||
this.threadCount = parseInt(futureMessageData.count);
|
||||
this.collapsed = (futureMessageData.collapsed === true);
|
||||
|
@ -61,6 +63,8 @@
|
|||
// Initialize tags form user's defaults
|
||||
if (Preferences.defaults.SOGoMailLabelsColors) {
|
||||
Message.$tags = Preferences.defaults.SOGoMailLabelsColors;
|
||||
} else {
|
||||
Message.$tags = {};
|
||||
}
|
||||
if (Preferences.defaults.SOGoMailDisplayRemoteInlineImages &&
|
||||
Preferences.defaults.SOGoMailDisplayRemoteInlineImages == 'always') {
|
||||
|
|
|
@ -148,7 +148,7 @@
|
|||
keys.push(sgHotkeys.createHotkey({
|
||||
key: hotkey,
|
||||
callback: _unlessInDialog(function($event) {
|
||||
if (vm.mailbox.$selectedCount() === 0)
|
||||
if (vm.mailbox.selectedCount() === 0)
|
||||
vm.deleteMessage();
|
||||
$event.preventDefault();
|
||||
}),
|
||||
|
@ -371,7 +371,7 @@
|
|||
else {
|
||||
state.go('mail.account.mailbox').then(function() {
|
||||
message = null;
|
||||
delete mailbox.selectedMessage;
|
||||
delete mailbox.$selectedMessage;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -446,7 +446,7 @@
|
|||
var destination = Mailbox.$virtualMode ? 'mail.account.virtualMailbox' : 'mail.account.mailbox';
|
||||
$state.go(destination).then(function() {
|
||||
vm.message = null;
|
||||
delete stateMailbox.selectedMessage;
|
||||
delete stateMailbox.$selectedMessage;
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@
|
|||
*/
|
||||
VirtualMailbox.prototype.resetSelectedMessage = function() {
|
||||
_.forEach(this.$mailboxes, function(mailbox) {
|
||||
delete mailbox.selectedMessage;
|
||||
delete mailbox.$selectedMessage;
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -134,7 +134,7 @@
|
|||
*/
|
||||
VirtualMailbox.prototype.hasSelectedMessage = function() {
|
||||
return angular.isDefined(_.find(this.$mailboxes, function(mailbox) {
|
||||
return angular.isDefined(mailbox.selectedMessage);
|
||||
return angular.isDefined(mailbox.$selectedMessage);
|
||||
}));
|
||||
};
|
||||
|
||||
|
@ -148,7 +148,7 @@
|
|||
*/
|
||||
VirtualMailbox.prototype.isSelectedMessage = function(messageId, mailboxPath) {
|
||||
return angular.isDefined(_.find(this.$mailboxes, function(mailbox) {
|
||||
return mailbox.path == mailboxPath && mailbox.selectedMessage == messageId;
|
||||
return mailbox.path == mailboxPath && mailbox.$selectedMessage == messageId;
|
||||
}));
|
||||
};
|
||||
|
||||
|
@ -216,7 +216,7 @@
|
|||
VirtualMailbox.prototype.$selectedMessageIndex = function() {
|
||||
var offset = 0;
|
||||
var selectedMailbox = _.find(this.$mailboxes, function(mailbox) {
|
||||
if (angular.isDefined(mailbox.selectedMessage)) {
|
||||
if (angular.isDefined(mailbox.$selectedMessage)) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
@ -224,7 +224,7 @@
|
|||
return false;
|
||||
}
|
||||
});
|
||||
return offset + selectedMailbox.uidsMap[selectedMailbox.selectedMessage];
|
||||
return offset + selectedMailbox.uidsMap[selectedMailbox.$selectedMessage];
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -233,23 +233,23 @@
|
|||
* @desc Return an associative array of the selected messages for each mailbox. Keys are the mailboxes ids.
|
||||
* @returns an associative array
|
||||
*/
|
||||
VirtualMailbox.prototype.$selectedMessages = function() {
|
||||
VirtualMailbox.prototype.selectedMessages = function() {
|
||||
var messagesMap = {};
|
||||
return _.filter(_.transform(this.$mailboxes, function(messagesMap, mailbox) {
|
||||
messagesMap[mailbox.id] = mailbox.$selectedMessages();
|
||||
messagesMap[mailbox.id] = mailbox.$selectedMessages;
|
||||
}, {}), function(o) {
|
||||
return _.size(o) > 0;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @function $selectedCount
|
||||
* @function selectedCount
|
||||
* @memberof VirtualMailbox.prototype
|
||||
* @desc Return the number of messages selected by the user.
|
||||
* @returns the number of selected messages
|
||||
*/
|
||||
VirtualMailbox.prototype.$selectedCount = function() {
|
||||
return _.sum(_.invokeMap(this.$mailboxes, '$selectedCount'));
|
||||
VirtualMailbox.prototype.selectedCount = function() {
|
||||
return _.sum(_.invokeMap(this.$mailboxes, 'selectedCount'));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
|
||||
this.$onInit = function () {
|
||||
var watchedAttrs = ['uid', 'isread', 'isflagged', 'flags', 'subject'];
|
||||
var watchedAttrs = ['uid', 'isread', 'isflagged', 'flags', 'subject', 'loading'];
|
||||
|
||||
// this.service = Message;
|
||||
this.MailboxService = Mailbox;
|
||||
|
@ -52,6 +52,11 @@
|
|||
|
||||
|
||||
this.onUpdate = function () {
|
||||
if (this.message.loading) {
|
||||
$element.addClass('sg-skeleton');
|
||||
return;
|
||||
}
|
||||
$element.removeClass('sg-skeleton');
|
||||
// Is the message unread?
|
||||
if (this.message.isread)
|
||||
$element.removeClass('unread');
|
||||
|
|
|
@ -121,71 +121,73 @@
|
|||
var i;
|
||||
$ctrl.message = $ctrl.parentController.message;
|
||||
|
||||
// Flags
|
||||
var flagList = $element[0].querySelector('.sg-category-dot-container'),
|
||||
$flagList = angular.element(flagList),
|
||||
flagElements = $mdUtil.nodesToArray(flagList.querySelectorAll('.sg-category-dot'));
|
||||
_.forEach(flagElements, function(flagElement) {
|
||||
flagList.removeChild(flagElement);
|
||||
});
|
||||
for (i = 0; i < $ctrl.message.flags.length && i < 5; i++) {
|
||||
var tag = $ctrl.message.flags[i];
|
||||
if ($ctrl.service.$tags[tag]) {
|
||||
var flagElement = angular.element('<div class="sg-category-dot"></div>');
|
||||
flagElement.css('background-color', $ctrl.service.$tags[tag][1]);
|
||||
$flagList.append(flagElement);
|
||||
if (!$ctrl.message.loading) {
|
||||
// Flags
|
||||
var flagList = $element[0].querySelector('.sg-category-dot-container'),
|
||||
$flagList = angular.element(flagList),
|
||||
flagElements = $mdUtil.nodesToArray(flagList.querySelectorAll('.sg-category-dot'));
|
||||
_.forEach(flagElements, function(flagElement) {
|
||||
flagList.removeChild(flagElement);
|
||||
});
|
||||
for (i = 0; i < $ctrl.message.flags.length && i < 5; i++) {
|
||||
var tag = $ctrl.message.flags[i];
|
||||
if ($ctrl.service.$tags[tag]) {
|
||||
var flagElement = angular.element('<div class="sg-category-dot"></div>');
|
||||
flagElement.css('background-color', $ctrl.service.$tags[tag][1]);
|
||||
$flagList.append(flagElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mailbox name when in virtual mode
|
||||
if ($ctrl.mailboxNameElement)
|
||||
$ctrl.mailboxNameElement.innerHTML = $ctrl.message.$mailbox.$displayName;
|
||||
// Mailbox name when in virtual mode
|
||||
if ($ctrl.mailboxNameElement)
|
||||
$ctrl.mailboxNameElement.innerHTML = $ctrl.message.$mailbox.$displayName;
|
||||
|
||||
// Sender or recipient when in
|
||||
if ($ctrl.MailboxService.selectedFolder.isSentFolder)
|
||||
$ctrl.senderElement.innerHTML = $ctrl.message.$shortAddress('to').encodeEntities();
|
||||
else
|
||||
$ctrl.senderElement.innerHTML = $ctrl.message.$shortAddress('from').encodeEntities();
|
||||
|
||||
// Priority icon
|
||||
if ($ctrl.message.priority && $ctrl.message.priority.level < 3) {
|
||||
$ctrl.priorityIconElement.classList.remove('ng-hide');
|
||||
if ($ctrl.message.priority.level < 2)
|
||||
$ctrl.priorityIconElement.classList.add('md-warn');
|
||||
// Sender or recipient when in
|
||||
if ($ctrl.MailboxService.selectedFolder.isSentFolder)
|
||||
$ctrl.senderElement.innerHTML = $ctrl.message.$shortAddress('to').encodeEntities();
|
||||
else
|
||||
$ctrl.priorityIconElement.classList.remove('md-warn');
|
||||
$ctrl.senderElement.innerHTML = $ctrl.message.$shortAddress('from').encodeEntities();
|
||||
|
||||
// Priority icon
|
||||
if ($ctrl.message.priority && $ctrl.message.priority.level < 3) {
|
||||
$ctrl.priorityIconElement.classList.remove('ng-hide');
|
||||
if ($ctrl.message.priority.level < 2)
|
||||
$ctrl.priorityIconElement.classList.add('md-warn');
|
||||
else
|
||||
$ctrl.priorityIconElement.classList.remove('md-warn');
|
||||
}
|
||||
else
|
||||
$ctrl.priorityIconElement.classList.add('ng-hide');
|
||||
|
||||
// Mail thread
|
||||
if ($ctrl.message.first) {
|
||||
$ctrl.threadButton.classList.remove('ng-hide');
|
||||
$ctrl.threadCountElement.innerHTML = $ctrl.message.threadCount;
|
||||
if ($ctrl.message.collapsed)
|
||||
$ctrl.threadIconElement.classList.remove('md-rotate-180-ccw');
|
||||
}
|
||||
else {
|
||||
$ctrl.threadButton.classList.add('ng-hide');
|
||||
}
|
||||
|
||||
// Subject
|
||||
$ctrl.subjectElement.innerHTML = $ctrl.message.subject.encodeEntities();
|
||||
|
||||
// Message size
|
||||
$ctrl.sizeElement.innerHTML = $ctrl.message.size;
|
||||
|
||||
// Received Date
|
||||
$ctrl.dateElement.innerHTML = $ctrl.message.relativedate;
|
||||
|
||||
setVisibility($ctrl.flagIconElement,
|
||||
$ctrl.message.isflagged);
|
||||
setVisibility($ctrl.answerIconElement,
|
||||
$ctrl.message.isanswered);
|
||||
setVisibility($ctrl.forwardIconElement,
|
||||
$ctrl.message.isforwarded);
|
||||
setVisibility($ctrl.attachmentIconElement,
|
||||
$ctrl.message.hasattachment);
|
||||
}
|
||||
else
|
||||
$ctrl.priorityIconElement.classList.add('ng-hide');
|
||||
|
||||
// Mail thread
|
||||
if ($ctrl.message.first) {
|
||||
$ctrl.threadButton.classList.remove('ng-hide');
|
||||
$ctrl.threadCountElement.innerHTML = $ctrl.message.threadCount;
|
||||
if ($ctrl.message.collapsed)
|
||||
$ctrl.threadIconElement.classList.remove('md-rotate-180-ccw');
|
||||
}
|
||||
else {
|
||||
$ctrl.threadButton.classList.add('ng-hide');
|
||||
}
|
||||
|
||||
// Subject
|
||||
$ctrl.subjectElement.innerHTML = $ctrl.message.subject.encodeEntities();
|
||||
|
||||
// Message size
|
||||
$ctrl.sizeElement.innerHTML = $ctrl.message.size;
|
||||
|
||||
// Received Date
|
||||
$ctrl.dateElement.innerHTML = $ctrl.message.relativedate;
|
||||
|
||||
setVisibility($ctrl.flagIconElement,
|
||||
$ctrl.message.isflagged);
|
||||
setVisibility($ctrl.answerIconElement,
|
||||
$ctrl.message.isanswered);
|
||||
setVisibility($ctrl.forwardIconElement,
|
||||
$ctrl.message.isforwarded);
|
||||
setVisibility($ctrl.attachmentIconElement,
|
||||
$ctrl.message.hasattachment);
|
||||
|
||||
// Call original method on parent controller
|
||||
angular.bind($ctrl.parentController, parentControllerOnUpdate)();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
(function() {
|
||||
/* jshint loopfunc: true */
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
|
@ -386,38 +387,50 @@
|
|||
if (this.nextInboxPoll)
|
||||
Preferences.$timeout.cancel(this.nextInboxPoll);
|
||||
|
||||
Preferences.$$resource.post('Mail', '0/folderINBOX/view', params).then(function(data) {
|
||||
var uids = data.uids;
|
||||
var uidHeaderIndex = data.headers[0].indexOf('uid');
|
||||
var fromHeaderIndex = data.headers[0].indexOf('From');
|
||||
var subjectHeaderIndex = data.headers[0].indexOf('Subject');
|
||||
if (data.threaded) {
|
||||
data.uids.splice(0, 1);
|
||||
uids = _.map(data.uids, 0);
|
||||
if (this.inboxSyncToken)
|
||||
params.syncToken = this.inboxSyncToken;
|
||||
|
||||
Preferences.$$resource.post('Mail', '0/folderINBOX/changes', params).then(function(data) {
|
||||
if (data.syncToken) {
|
||||
_this.inboxSyncToken = data.syncToken;
|
||||
Preferences.$log.debug("New syncToken is " + _this.inboxSyncToken);
|
||||
}
|
||||
if (_this.lastUid) {
|
||||
_.find(uids, function (uid, index) {
|
||||
var headers, id, href, toast;
|
||||
if (uid > _this.lastUid) {
|
||||
|
||||
if (angular.isDefined(data.headers) && data.headers.length > 0) {
|
||||
var uidHeaderIndex = data.headers[0].indexOf('uid');
|
||||
var isReadHeaderIndex = data.headers[0].indexOf('isRead');
|
||||
var fromHeaderIndex = data.headers[0].indexOf('From');
|
||||
var subjectHeaderIndex = data.headers[0].indexOf('Subject');
|
||||
var i;
|
||||
var showToast = function() {
|
||||
var _this = this;
|
||||
return Preferences.$toast.show(this)
|
||||
.then(function(response) {
|
||||
if (response === 'ok') {
|
||||
_this.viewInboxMessage(_this.locals.uid);
|
||||
}
|
||||
});
|
||||
};
|
||||
for (i = 1; i < data.headers.length; i++) {
|
||||
var headers = data.headers[i],
|
||||
uid = headers[uidHeaderIndex],
|
||||
id, href, toast;
|
||||
if (!headers[isReadHeaderIndex]) {
|
||||
// New unseen message
|
||||
Preferences.$log.debug('Show notification for message ' + uid);
|
||||
headers = _.find(data.headers, function(h) {
|
||||
return h[uidHeaderIndex] == uid;
|
||||
});
|
||||
if (_this.defaults.SOGoDesktopNotifications) {
|
||||
id = 'mail-inbox-' + uid;
|
||||
href = Preferences.$state.href('mail.account.mailbox.message', { accountId: 0, mailboxId: 'INBOX', messageId: uid });
|
||||
_this.createNotification(id, headers[subjectHeaderIndex], {
|
||||
body: headers[fromHeaderIndex][0].name || headers[fromHeaderIndex][0].email,
|
||||
icon: '/SOGo.woa/WebServerResources/img/email-256px.png',
|
||||
onClick: function () {
|
||||
_this.viewInboxMessage(uid);
|
||||
}
|
||||
onClick: angular.bind(_this, _this.viewInboxMessage, uid)
|
||||
});
|
||||
}
|
||||
else {
|
||||
toast = {
|
||||
locals: {
|
||||
uid: uid,
|
||||
title: headers[subjectHeaderIndex],
|
||||
body: headers[fromHeaderIndex][0].name || headers[fromHeaderIndex][0].email
|
||||
},
|
||||
|
@ -440,28 +453,13 @@
|
|||
].join(''),
|
||||
position: 'top right',
|
||||
hideDelay: 5000,
|
||||
controller: toastController
|
||||
controller: toastController,
|
||||
viewInboxMessage: _this.viewInboxMessage
|
||||
};
|
||||
_this.currentToast = _this.currentToast.then(function () {
|
||||
return Preferences.$toast.show(toast)
|
||||
.then(function(response) {
|
||||
if (response === 'ok') {
|
||||
_this.viewInboxMessage(uid);
|
||||
}
|
||||
});
|
||||
});
|
||||
_this.currentToast = _this.currentToast.then(angular.bind(toast, showToast));
|
||||
}
|
||||
return false; // Continue to next unseen message
|
||||
}
|
||||
else {
|
||||
return true; // No more new messages
|
||||
}
|
||||
});
|
||||
if (uids[0] > _this.lastUid) {
|
||||
_this.lastUid = uids[0];
|
||||
}
|
||||
} else {
|
||||
_this.lastUid = uids[0];
|
||||
}
|
||||
}).finally(function () {
|
||||
var refreshViewCheck = _this.defaults.SOGoRefreshViewCheck;
|
||||
|
|
|
@ -51,11 +51,33 @@
|
|||
|
||||
.unread {
|
||||
.#{$md}-subhead,
|
||||
.#{$md}-body {
|
||||
.#{$md}-body,
|
||||
.sg-tile-date {
|
||||
font-weight: $sg-font-medium;
|
||||
}
|
||||
.sg-tile-date {
|
||||
color: sg-color($sogoBlue, 600);
|
||||
}
|
||||
|
||||
.sg-skeleton {
|
||||
background-color: inherit;
|
||||
sg-avatar-image, .sg-category-dot-container, md-icon, .sg-tile-btn {
|
||||
display: none;
|
||||
}
|
||||
.md-icon-button, .sg-md-subhead, .sg-md-body {
|
||||
color: $colorGrey300;
|
||||
background-color: $colorGrey300;
|
||||
font-size: 0;
|
||||
}
|
||||
.sg-md-subhead, .sg-md-body {
|
||||
margin-bottom: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.sg-md-subhead {
|
||||
height: 1.2rem;
|
||||
width: 45%;
|
||||
}
|
||||
.sg-md-body {
|
||||
height: 1rem;
|
||||
width: 70%;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue