(web) Restore mail threads

Fixes #3478
Fixes #4616
Fixes #4735
pull/257/head
Francis Lachapelle 2019-06-27 17:43:11 -04:00
parent cc6fcc5fbd
commit b5949752b4
10 changed files with 142 additions and 20 deletions

1
NEWS
View File

@ -9,6 +9,7 @@ Enhancements
- [web] add Indonesian (id) translation
- [web] updated Angular Material to version 1.1.19
- [web] replaced bower packages by npm packages
- [web] restored mail threads (#3478, #4616, #4735)
Bug fixes
- [web] fixed wrong translation of custom calendar categories

View File

@ -18,6 +18,7 @@
* Boston, MA 02111-1307, USA.
*/
#import <Foundation/NSURL.h>
#import <Foundation/NSValue.h>
#import <NGObjWeb/WOContext+SoObjects.h>
@ -221,16 +222,30 @@
- (void) collapseAction: (BOOL) isCollapsing
{
WORequest *request;
NSMutableDictionary *moduleSettings, *threadsCollapsed, *content;
NSArray *currentComponents;
NSMutableArray *mailboxThreadsCollapsed;
NSString *msguid, *keyForMsgUIDs;
NSMutableDictionary *moduleSettings, *threadsCollapsed;
NSString *accountName, *msguid, *keyForMsgUIDs;
SOGoMailAccount *account;
SOGoMailFolder *mailbox;
SOGoMailObject *co;
SOGoUserSettings *us;
int count;
request = [context request];
content = [[request contentAsString] objectFromJSONString];
keyForMsgUIDs = [content objectForKey:@"currentMailbox"];
msguid = [content objectForKey:@"msguid"];
co = [self clientObject];
account = [co mailAccountFolder];
accountName = [account nameInContainer];
mailbox = [co container];
msguid = [co nameInContainer];
// Build lookup key for current mailbox path
currentComponents = [[mailbox imap4URL] pathComponents];
count = [currentComponents count];
currentComponents = [[currentComponents subarrayWithRange: NSMakeRange(1,count-1)]
resultsOfSelector: @selector (asCSSIdentifier)];
currentComponents = [currentComponents stringsWithFormat: @"folder%@"];
keyForMsgUIDs = [NSString stringWithFormat:@"/%@/%@", accountName,
[currentComponents componentsJoinedByString: @"/"]];
us = [[context activeUser] userSettings];
if (!(moduleSettings = [us objectForKey: @"Mail"]))

View File

@ -300,7 +300,7 @@
<md-list-item
aria-label="{{currentMessage.subject}}"
class="sg-message-list-item"
ng-class="{'md-default-theme md-accent md-bg md-hue-2': mailbox.selectedFolder.isSelectedMessage(currentMessage.uid, currentMessage.$mailbox.path)}"
ng-class="{'md-default-theme md-accent md-bg md-hue-2': mailbox.selectedFolder.isSelectedMessage(currentMessage.uid, currentMessage.$mailbox.path), 'sg-message-thread-first': currentMessage.first, 'sg-message-thread': currentMessage.threadMember}"
md-virtual-repeat="currentMessage in mailbox.service.selectedFolder"
md-on-demand="md-on-demand" md-item-size="56"
ng-click="mailbox.selectMessage(currentMessage)"

View File

@ -578,7 +578,7 @@
</md-checkbox>
</div>
<div class="ng-hide">
<div>
<md-checkbox
ng-model="app.preferences.defaults.SOGoMailSortByThreads"
ng-true-value="1"

View File

@ -266,6 +266,16 @@
'}'
].join(''));
// Register custom stylesheet for Mail module
$mdThemingProvider.registerStyles([
'.sg-message-thread {',
' background-color: \'{{primary-100}}\';',
'}',
'.sg-message-thread-first {',
' background-color: \'{{primary-200}}\';',
'}',
].join(''));
if (!window.DebugEnabled) {
// Disable debugging information
$logProvider.debugEnabled(false);

View File

@ -165,6 +165,9 @@
if (this.path) {
this.id = this.$id();
this.$acl = new Mailbox.$$Acl('Mail/' + this.id);
if (this.threaded) {
this.$collapsedThreads = Mailbox.$Preferences.settings.Mail.threadsCollapsed['/' + this.id] || [];
}
}
this.$displayName = this.name;
if (this.type) {
@ -222,7 +225,16 @@
* @returns the number of messages in the mailbox
*/
Mailbox.prototype.getLength = function() {
return this.$messages.length;
var _this = this, collapsedThread = false;
var visibleMessages = _.filter(this.$messages, function(msg, i) {
if (msg.first) {
collapsedThread = msg.collapsed;
} else if (msg.level < 0) {
collapsedThread = false;
}
return msg.first || collapsedThread === false;
});
return visibleMessages.length;
};
/**
@ -232,10 +244,18 @@
* @returns the message at the specified index
*/
Mailbox.prototype.getItemAtIndex = function(index) {
var message;
var _this = this, collapsedThread = false, message;
var visibleMessages = _.filter(this.$messages, function(msg, i) {
if (msg.first) {
collapsedThread = msg.collapsed;
} else if (msg.level < 0) {
collapsedThread = false; // leaving the thread
}
return msg.first || collapsedThread === false;
});
if (index >= 0 && index < this.$messages.length) {
message = this.$messages[index];
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))
@ -928,7 +948,7 @@
// First entry of 'uids' are keys when threaded view is enabled
if (_this.threaded) {
uids = _this.uids[0];
uids = _this.uids[0]; // uid, level, first
_this.uids.splice(0, 1);
}
@ -937,6 +957,19 @@
var data, msgObject;
if (_this.threaded) {
data = _.zipObject(uids, msg);
if (data.first === 1) {
var count = 1;
while (_this.uids[i + count] &&
_this.uids[i + count][1] >= 0 &&
_this.uids[i + count][2] !== 1) {
count++;
}
data.count = count;
data.collapsed = false;
if (_this.$collapsedThreads.indexOf(data.uid.toString()) >= 0) {
data.collapsed = true;
}
}
} else {
data = {uid: msg.toString()};
}

View File

@ -26,6 +26,15 @@
this.init(futureMessageData);
}
this.uid = parseInt(futureMessageData.uid);
this.level = parseInt(futureMessageData.level);
this.first = parseInt(futureMessageData.first) === 1;
if (this.first) {
this.threadCount = parseInt(futureMessageData.count);
this.collapsed = (futureMessageData.collapsed === true);
}
else if (!isNaN(this.level) && this.level >= 0) {
this.threadMember = true;
}
}
else {
// The promise will be unwrapped first
@ -539,7 +548,7 @@
};
/**
* @function $markAsFlaggedOrUnflagged
* @function toggleFlag
* @memberof Message.prototype
* @desc Add or remove a the \\Flagged flag on the current message.
* @returns a promise of the HTTP operation
@ -558,6 +567,24 @@
});
};
/**
* @function toggleThread
* @memberof Message.prototype
* @desc Collapse or expand mail thread
* @returns a promise of the HTTP operation
*/
Message.prototype.toggleThread = function() {
var _this = this,
action = 'markMessageCollapse';
if (this.collapsed)
action = 'markMessageUncollapse';
this.collapsed = !this.collapsed;
return Message.$$resource.post(this.$absolutePath(), action);
};
/**
* @function $isLoading
* @memberof Message.prototype

View File

@ -26,6 +26,9 @@
' <div class="sg-md-body">',
' <div class="sg-tile-subject"><!-- subject --></div>',
' <div class="sg-tile-size"><!-- size --></div>',
' <md-button class="sg-tile-thread md-secondary ng-hide" md-colors="::{ color: \'accent-600\'}" ng-click="$ctrl.toggleThread()">',
' <md-icon class="md-rotate-180-ccw" md-colors="::{ color: \'accent-600\'}">expand_more</md-icon><span><span>', // expanded by default (icon is rotated)
' </md-button>',
' </div>',
'</div>',
'<div class="sg-tile-icons">',
@ -59,7 +62,7 @@
var $ctrl = this;
this.$postLink = function () {
var contentDivElement, iconsDivElement;
var contentDivElement, threadButton, iconsDivElement;
var parentControllerOnUpdate, setVisibility;
this.parentController = $scope.parentController;
@ -74,6 +77,12 @@
iconsDivElement = angular.element(div);
});
threadButton = contentDivElement.find('button')[0];
this.threadButton = threadButton;
threadButton = angular.element(threadButton);
this.threadIconElement = threadButton.find('md-icon')[0];
this.threadCountElement = threadButton.find('span')[0];
this.priorityIconElement = contentDivElement.find('md-icon')[0];
if (Mailbox.$virtualMode) {
@ -147,9 +156,19 @@
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;
@ -173,6 +192,14 @@
this.MailboxService = Mailbox;
};
this.toggleThread = function() {
if (this.message.collapsed)
this.threadIconElement.classList.add('md-rotate-180-ccw');
else
this.threadIconElement.classList.remove('md-rotate-180-ccw');
this.message.toggleThread();
};
}

View File

@ -11,7 +11,7 @@
var _this = this, defaultsElement, settingsElement, data;
this.defaults = {};
this.settings = {};
this.settings = {Mail: {}};
defaultsElement = Preferences.$document[0].getElementById('UserDefaults');
if (defaultsElement) {

View File

@ -172,16 +172,25 @@ div.md-tile-left {
&-tile-content {
flex: 1;
overflow: hidden; // required in Firefox
.sg-tile-date, .sg-tile-size {
.sg-tile-date, .sg-tile-size, .sg-tile-thread {
flex-shrink: 0;
font-size: sg-size(body);
font-weight: $sg-font-light;
line-height: $sg-line-height-2;
margin-left: 3px;
margin-left: 3px !important;
}
.sg-tile-size {
font-size: sg-size(caption);
}
.sg-tile-thread {
min-height: auto;
min-width: auto;
padding: 0 3px !important;
font-weight: $sg-font-medium;
md-icon {
font-size: sg-size(body);
}
}
.#{$md}-subhead {
@extend .#{$md}-body-1;
white-space: pre;