parent
cc6fcc5fbd
commit
b5949752b4
1
NEWS
1
NEWS
|
@ -9,6 +9,7 @@ Enhancements
|
||||||
- [web] add Indonesian (id) translation
|
- [web] add Indonesian (id) translation
|
||||||
- [web] updated Angular Material to version 1.1.19
|
- [web] updated Angular Material to version 1.1.19
|
||||||
- [web] replaced bower packages by npm packages
|
- [web] replaced bower packages by npm packages
|
||||||
|
- [web] restored mail threads (#3478, #4616, #4735)
|
||||||
|
|
||||||
Bug fixes
|
Bug fixes
|
||||||
- [web] fixed wrong translation of custom calendar categories
|
- [web] fixed wrong translation of custom calendar categories
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
* Boston, MA 02111-1307, USA.
|
* Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/NSURL.h>
|
||||||
#import <Foundation/NSValue.h>
|
#import <Foundation/NSValue.h>
|
||||||
|
|
||||||
#import <NGObjWeb/WOContext+SoObjects.h>
|
#import <NGObjWeb/WOContext+SoObjects.h>
|
||||||
|
@ -221,16 +222,30 @@
|
||||||
|
|
||||||
- (void) collapseAction: (BOOL) isCollapsing
|
- (void) collapseAction: (BOOL) isCollapsing
|
||||||
{
|
{
|
||||||
WORequest *request;
|
NSArray *currentComponents;
|
||||||
NSMutableDictionary *moduleSettings, *threadsCollapsed, *content;
|
|
||||||
NSMutableArray *mailboxThreadsCollapsed;
|
NSMutableArray *mailboxThreadsCollapsed;
|
||||||
NSString *msguid, *keyForMsgUIDs;
|
NSMutableDictionary *moduleSettings, *threadsCollapsed;
|
||||||
|
NSString *accountName, *msguid, *keyForMsgUIDs;
|
||||||
|
SOGoMailAccount *account;
|
||||||
|
SOGoMailFolder *mailbox;
|
||||||
|
SOGoMailObject *co;
|
||||||
SOGoUserSettings *us;
|
SOGoUserSettings *us;
|
||||||
|
int count;
|
||||||
|
|
||||||
request = [context request];
|
co = [self clientObject];
|
||||||
content = [[request contentAsString] objectFromJSONString];
|
account = [co mailAccountFolder];
|
||||||
keyForMsgUIDs = [content objectForKey:@"currentMailbox"];
|
accountName = [account nameInContainer];
|
||||||
msguid = [content objectForKey:@"msguid"];
|
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];
|
us = [[context activeUser] userSettings];
|
||||||
if (!(moduleSettings = [us objectForKey: @"Mail"]))
|
if (!(moduleSettings = [us objectForKey: @"Mail"]))
|
||||||
|
|
|
@ -300,7 +300,7 @@
|
||||||
<md-list-item
|
<md-list-item
|
||||||
aria-label="{{currentMessage.subject}}"
|
aria-label="{{currentMessage.subject}}"
|
||||||
class="sg-message-list-item"
|
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-virtual-repeat="currentMessage in mailbox.service.selectedFolder"
|
||||||
md-on-demand="md-on-demand" md-item-size="56"
|
md-on-demand="md-on-demand" md-item-size="56"
|
||||||
ng-click="mailbox.selectMessage(currentMessage)"
|
ng-click="mailbox.selectMessage(currentMessage)"
|
||||||
|
|
|
@ -578,7 +578,7 @@
|
||||||
</md-checkbox>
|
</md-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ng-hide">
|
<div>
|
||||||
<md-checkbox
|
<md-checkbox
|
||||||
ng-model="app.preferences.defaults.SOGoMailSortByThreads"
|
ng-model="app.preferences.defaults.SOGoMailSortByThreads"
|
||||||
ng-true-value="1"
|
ng-true-value="1"
|
||||||
|
|
|
@ -266,6 +266,16 @@
|
||||||
'}'
|
'}'
|
||||||
].join(''));
|
].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) {
|
if (!window.DebugEnabled) {
|
||||||
// Disable debugging information
|
// Disable debugging information
|
||||||
$logProvider.debugEnabled(false);
|
$logProvider.debugEnabled(false);
|
||||||
|
|
|
@ -165,6 +165,9 @@
|
||||||
if (this.path) {
|
if (this.path) {
|
||||||
this.id = this.$id();
|
this.id = this.$id();
|
||||||
this.$acl = new Mailbox.$$Acl('Mail/' + 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;
|
this.$displayName = this.name;
|
||||||
if (this.type) {
|
if (this.type) {
|
||||||
|
@ -222,7 +225,16 @@
|
||||||
* @returns the number of messages in the mailbox
|
* @returns the number of messages in the mailbox
|
||||||
*/
|
*/
|
||||||
Mailbox.prototype.getLength = function() {
|
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
|
* @returns the message at the specified index
|
||||||
*/
|
*/
|
||||||
Mailbox.prototype.getItemAtIndex = function(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) {
|
if (index >= 0 && index < visibleMessages.length) {
|
||||||
message = this.$messages[index];
|
message = visibleMessages[index];
|
||||||
this.$lastVisibleIndex = Math.max(0, index - 3); // Magic number is NUM_EXTRA from virtual-repeater.js
|
this.$lastVisibleIndex = Math.max(0, index - 3); // Magic number is NUM_EXTRA from virtual-repeater.js
|
||||||
|
|
||||||
if (this.$loadMessage(message.uid))
|
if (this.$loadMessage(message.uid))
|
||||||
|
@ -928,7 +948,7 @@
|
||||||
|
|
||||||
// First entry of 'uids' are keys when threaded view is enabled
|
// First entry of 'uids' are keys when threaded view is enabled
|
||||||
if (_this.threaded) {
|
if (_this.threaded) {
|
||||||
uids = _this.uids[0];
|
uids = _this.uids[0]; // uid, level, first
|
||||||
_this.uids.splice(0, 1);
|
_this.uids.splice(0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -937,6 +957,19 @@
|
||||||
var data, msgObject;
|
var data, msgObject;
|
||||||
if (_this.threaded) {
|
if (_this.threaded) {
|
||||||
data = _.zipObject(uids, msg);
|
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 {
|
} else {
|
||||||
data = {uid: msg.toString()};
|
data = {uid: msg.toString()};
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,15 @@
|
||||||
this.init(futureMessageData);
|
this.init(futureMessageData);
|
||||||
}
|
}
|
||||||
this.uid = parseInt(futureMessageData.uid);
|
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 {
|
else {
|
||||||
// The promise will be unwrapped first
|
// The promise will be unwrapped first
|
||||||
|
@ -539,7 +548,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function $markAsFlaggedOrUnflagged
|
* @function toggleFlag
|
||||||
* @memberof Message.prototype
|
* @memberof Message.prototype
|
||||||
* @desc Add or remove a the \\Flagged flag on the current message.
|
* @desc Add or remove a the \\Flagged flag on the current message.
|
||||||
* @returns a promise of the HTTP operation
|
* @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
|
* @function $isLoading
|
||||||
* @memberof Message.prototype
|
* @memberof Message.prototype
|
||||||
|
|
|
@ -26,6 +26,9 @@
|
||||||
' <div class="sg-md-body">',
|
' <div class="sg-md-body">',
|
||||||
' <div class="sg-tile-subject"><!-- subject --></div>',
|
' <div class="sg-tile-subject"><!-- subject --></div>',
|
||||||
' <div class="sg-tile-size"><!-- size --></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>',
|
'</div>',
|
||||||
'<div class="sg-tile-icons">',
|
'<div class="sg-tile-icons">',
|
||||||
|
@ -59,7 +62,7 @@
|
||||||
var $ctrl = this;
|
var $ctrl = this;
|
||||||
|
|
||||||
this.$postLink = function () {
|
this.$postLink = function () {
|
||||||
var contentDivElement, iconsDivElement;
|
var contentDivElement, threadButton, iconsDivElement;
|
||||||
var parentControllerOnUpdate, setVisibility;
|
var parentControllerOnUpdate, setVisibility;
|
||||||
|
|
||||||
this.parentController = $scope.parentController;
|
this.parentController = $scope.parentController;
|
||||||
|
@ -74,6 +77,12 @@
|
||||||
iconsDivElement = angular.element(div);
|
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];
|
this.priorityIconElement = contentDivElement.find('md-icon')[0];
|
||||||
|
|
||||||
if (Mailbox.$virtualMode) {
|
if (Mailbox.$virtualMode) {
|
||||||
|
@ -147,9 +156,19 @@
|
||||||
else
|
else
|
||||||
$ctrl.priorityIconElement.classList.add('ng-hide');
|
$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
|
// Subject
|
||||||
$ctrl.subjectElement.innerHTML = $ctrl.message.subject.encodeEntities();
|
$ctrl.subjectElement.innerHTML = $ctrl.message.subject.encodeEntities();
|
||||||
|
|
||||||
// Message size
|
// Message size
|
||||||
$ctrl.sizeElement.innerHTML = $ctrl.message.size;
|
$ctrl.sizeElement.innerHTML = $ctrl.message.size;
|
||||||
|
|
||||||
|
@ -173,6 +192,14 @@
|
||||||
this.MailboxService = Mailbox;
|
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();
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
var _this = this, defaultsElement, settingsElement, data;
|
var _this = this, defaultsElement, settingsElement, data;
|
||||||
|
|
||||||
this.defaults = {};
|
this.defaults = {};
|
||||||
this.settings = {};
|
this.settings = {Mail: {}};
|
||||||
|
|
||||||
defaultsElement = Preferences.$document[0].getElementById('UserDefaults');
|
defaultsElement = Preferences.$document[0].getElementById('UserDefaults');
|
||||||
if (defaultsElement) {
|
if (defaultsElement) {
|
||||||
|
|
|
@ -172,16 +172,25 @@ div.md-tile-left {
|
||||||
&-tile-content {
|
&-tile-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden; // required in Firefox
|
overflow: hidden; // required in Firefox
|
||||||
.sg-tile-date, .sg-tile-size {
|
.sg-tile-date, .sg-tile-size, .sg-tile-thread {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
font-size: sg-size(body);
|
font-size: sg-size(body);
|
||||||
font-weight: $sg-font-light;
|
font-weight: $sg-font-light;
|
||||||
line-height: $sg-line-height-2;
|
line-height: $sg-line-height-2;
|
||||||
margin-left: 3px;
|
margin-left: 3px !important;
|
||||||
}
|
}
|
||||||
.sg-tile-size {
|
.sg-tile-size {
|
||||||
font-size: sg-size(caption);
|
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 {
|
.#{$md}-subhead {
|
||||||
@extend .#{$md}-body-1;
|
@extend .#{$md}-body-1;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
|
|
Loading…
Reference in New Issue