Add some basic logic (very incomplete/buggy) for cross-folder
threading to the thread processor. Does not attempt to handle emails duplicated across folders. Add preference to turn on experimental cross-folder threading. Fix hiding rows in preferences panel. Open-webOS-DCO-1.0-Signed-off-by: Jason Lai <jason.lai@palm.com> Change-Id: I7d61bdc082de6149df4570bb51b6a22f5b7e8ba7
This commit is contained in:
1 parent
6fc6f14350
commit
f58539e16b
6 files changed
+164
-40
No files matched your search
@@ -302,6 +302,16 @@ enyo.kind({
|
||||
var tomorrowStart = todayStart + 86400000; // (1000 * 60 * 60 *24)
|
||||
return timestamp >= todayStart && timestamp < tomorrowStart;
|
||||
},
|
||||
|
||||
formatSender: function (obj) {
|
||||
var account = this.getCurrentAccount();
|
||||
|
||||
if (account && obj.addr && obj.addr === account.getEmailAddress()) {
|
||||
return $L("me");
|
||||
} else {
|
||||
return obj.name || obj.addr || "";
|
||||
}
|
||||
},
|
||||
|
||||
// Renders a row in the email list
|
||||
// NOTE: as an optimization, most of the item components have captureState: false set.
|
||||
@@ -345,8 +355,7 @@ enyo.kind({
|
||||
var senderNames = [];
|
||||
|
||||
for (var si = 0; si < senders.length; si += 1) {
|
||||
sender = senders[si];
|
||||
senderNames.push(sender.name || sender.addr || "");
|
||||
senderNames.push(this.formatSender(senders[si]));
|
||||
}
|
||||
sendersText = senderNames.join(", ");
|
||||
|
||||
@@ -355,8 +364,7 @@ enyo.kind({
|
||||
}
|
||||
} else {
|
||||
// get most recent sender
|
||||
sender = senders[0];
|
||||
sendersText = sender.name || sender.addr || "";
|
||||
sendersText = this.formatSender(senders[0]);
|
||||
}
|
||||
senderLine.setContent(sendersText);
|
||||
|
||||
|
||||
@@ -114,6 +114,20 @@ enyo.kind({
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
kind: "LabeledContainer",
|
||||
name: "crossFolderThreadingBlock",
|
||||
caption: $L("Thread Emails Across Folders (EXPERIMENTAL)"),
|
||||
components: [
|
||||
{
|
||||
kind: "ToggleButton",
|
||||
name: "crossFolderThreading",
|
||||
state: false,
|
||||
onChange: "toggleCrossFolderThreading",
|
||||
style: "display: inline-block"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
kind: "LabeledContainer",
|
||||
caption: $L("Delete Confirmation"),
|
||||
@@ -163,7 +177,6 @@ enyo.kind({
|
||||
{
|
||||
kind: "LabeledContainer",
|
||||
name: "threadViewOldestFirstBlock",
|
||||
showing: false,
|
||||
caption: $L("Show Older Emails In Thread First"),
|
||||
components: [
|
||||
{
|
||||
@@ -268,6 +281,21 @@ enyo.kind({
|
||||
toggleConfirmDelete: function () {
|
||||
this.setPref('confirmDeleteOnSwipe', this.$.confirmDeletes.getState());
|
||||
},
|
||||
|
||||
setRowVisible: function (control, showing) {
|
||||
var group = control.getContainer();
|
||||
var index = group.indexOfControl(control);
|
||||
|
||||
if (!control) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (showing) {
|
||||
group.showRow(index);
|
||||
} else {
|
||||
group.hideRow(index);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Turn on/off email threading functionality
|
||||
@@ -278,7 +306,8 @@ enyo.kind({
|
||||
|
||||
this.setPref('emailThreading', this.$.emailThreading.getState());
|
||||
|
||||
this.$.threadViewOldestFirstBlock.setShowing(enabled);
|
||||
this.setRowVisible(this.$.crossFolderThreadingBlock, enabled);
|
||||
this.setRowVisible(this.$.threadViewOldestFirstBlock, enabled);
|
||||
|
||||
if (enabled && !wasEnabled) {
|
||||
// need to build thread index
|
||||
@@ -289,6 +318,12 @@ enyo.kind({
|
||||
}
|
||||
},
|
||||
|
||||
toggleCrossFolderThreading: function () {
|
||||
this.setPref('crossFolderThreading', this.$.crossFolderThreading.getState());
|
||||
|
||||
enyo.application.threader.rebuildIndex();
|
||||
},
|
||||
|
||||
/**
|
||||
* Change order that messages in a thread are diplayed
|
||||
*/
|
||||
@@ -399,8 +434,11 @@ enyo.kind({
|
||||
|
||||
this.$.emailThreading.setState(threadingEnabled);
|
||||
|
||||
this.setRowVisible(this.$.crossFolderThreadingBlock, threadingEnabled);
|
||||
this.$.threadViewOldestFirst.setState(prefs.get('crossFolderThreading') || false);
|
||||
|
||||
// Message view options
|
||||
this.$.threadViewOldestFirstBlock.setShowing(threadingEnabled);
|
||||
this.setRowVisible(this.$.threadViewOldestFirstBlock, threadingEnabled);
|
||||
this.$.threadViewOldestFirst.setState(prefs.get('threadViewOldestFirst') || false);
|
||||
|
||||
this.$.hideAccountsOnViewEmail.setState(prefs.get('hideAccountsOnViewEmail') || false);
|
||||
|
||||
@@ -61,6 +61,28 @@ VirtualConversation.prototype = {
|
||||
return this._data.accountId;
|
||||
}
|
||||
},
|
||||
|
||||
getFolderKeys: function () {
|
||||
if (this.emailData) {
|
||||
return [this.emailData.folderId];
|
||||
} else {
|
||||
return this.convData.folderKeys;
|
||||
}
|
||||
},
|
||||
|
||||
getFolderIds: function () {
|
||||
return enyo.filter(this.getFolderKeys(), function (key) {
|
||||
return key[0] !== '#' && key[1] !== '#';
|
||||
});
|
||||
},
|
||||
|
||||
hasFolderKey: function (key) {
|
||||
if (this.emailData) {
|
||||
return key === this.emailData.folderId;
|
||||
} else {
|
||||
return enyo.indexOf(this.convData.folderKeys) !== -1;
|
||||
}
|
||||
},
|
||||
|
||||
getFolderId: function () {
|
||||
return this._data.folderId;
|
||||
|
||||
@@ -69,27 +69,32 @@ enyo.kind({
|
||||
this.changedProps.priority = true;
|
||||
}
|
||||
},
|
||||
|
||||
addToResultsSet: function (items, hash) {
|
||||
if (!items) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < items.length; i += 1) {
|
||||
var item = items[i];
|
||||
|
||||
if (hash[item]) {
|
||||
hash[item].push(this);
|
||||
} else {
|
||||
hash[item] = [this];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
addToResults: function (convResultsHash, key) {
|
||||
var i;
|
||||
|
||||
var convResults = convResultsHash[key] ||
|
||||
{ messageIdHash: {}, threadTopicHash: {}, serverConversationIds: {} };
|
||||
|
||||
var messageIds = this.data.messageIds || [];
|
||||
for (i = 0; i < messageIds.length; i += 1) {
|
||||
convResults.messageIdHash[ messageIds[i] ] = this;
|
||||
}
|
||||
|
||||
var threadTopics = this.data.threadTopics || [];
|
||||
for (i = 0; i < threadTopics.length; i += 1) {
|
||||
convResults.threadTopicHash[ threadTopics[i] ] = this;
|
||||
}
|
||||
|
||||
var serverConversationIds = this.data.serverConversationIds || [];
|
||||
for (i = 0; i < serverConversationIds.length; i += 1) {
|
||||
convResults.serverConversationIdHash [ serverConversationIds[i] ] = this;
|
||||
}
|
||||
|
||||
this.addToResultsSet(this.data.messageIds, convResults.messageIdHash);
|
||||
this.addToResultsSet(this.data.threadTopics, convResults.threadTopicHash);
|
||||
this.addToResultsSet(this.data.serverConversationIds, convResults.serverConversationIdHash);
|
||||
|
||||
convResultsHash[key] = convResults;
|
||||
},
|
||||
|
||||
@@ -163,8 +163,7 @@ enyo.kind({
|
||||
// - thread merging?
|
||||
// - prevent merging emails in trash
|
||||
useCrossFolderThreading: function () {
|
||||
//return this.processorState.crossFolderThreading;
|
||||
return false;
|
||||
return this.processorState.crossFolderThreading;
|
||||
},
|
||||
|
||||
// [public]
|
||||
@@ -233,7 +232,8 @@ enyo.kind({
|
||||
name: this.PROCESSOR_NAME,
|
||||
lastProcessedRev: 0,
|
||||
addDeleteCount: 0,
|
||||
threadingEnabled: EmailApp.Util.isThreadingEnabled()
|
||||
threadingEnabled: EmailApp.Util.isThreadingEnabled(),
|
||||
crossFolderThreading: enyo.application.prefs.get("crossFolderThreading") || false
|
||||
};
|
||||
|
||||
// And I've tried to make it come alive
|
||||
@@ -275,6 +275,7 @@ enyo.kind({
|
||||
delete this.pendingRequests.wipe;
|
||||
|
||||
this.processorState.threadingEnabled = EmailApp.Util.isThreadingEnabled();
|
||||
this.processorState.crossFolderThreading = enyo.application.prefs.get("crossFolderThreading") || false;
|
||||
this.setLastProcessedRev(-1);
|
||||
this.runCommand("DisableThreadingCommand", {});
|
||||
return;
|
||||
@@ -352,6 +353,7 @@ enyo.kind({
|
||||
lastProcessedRev: this.getLastProcessedRev(),
|
||||
addDeleteCount: this.processorState.addDeleteCount || 0,
|
||||
threadingEnabled: this.processorState.threadingEnabled,
|
||||
crossFolderThreading: this.processorState.crossFolderThreading,
|
||||
accounts: this.processorState.accounts || {}
|
||||
};
|
||||
|
||||
|
||||
@@ -136,7 +136,8 @@ enyo.kind({
|
||||
}
|
||||
|
||||
// Set withinInbox for inbox emails -- used for non-threaded all inboxes support
|
||||
var account = enyo.application.accounts.getAccount(email.accountId);
|
||||
var accountId = enyo.application.folderProcessor.getFolderAccount(email.folderId);
|
||||
var account = enyo.application.accounts.getAccount(accountId);
|
||||
|
||||
var withinInbox = account && account.getInboxFolderId() === email.folderId;
|
||||
if (email.withinInbox !== withinInbox) {
|
||||
@@ -372,17 +373,27 @@ enyo.kind({
|
||||
|
||||
this.matchConversations();
|
||||
},
|
||||
|
||||
// decide if an email should be threaded
|
||||
shouldThreadEmail: function (email) {
|
||||
var accountId = enyo.application.folderProcessor.getFolderAccount(email.folderId);
|
||||
var account = enyo.application.accounts.getAccount(accountId);
|
||||
|
||||
if (account.getDraftsFolderId() === email.folderId ||
|
||||
account.getTrashFolderId() === email.folderId) {
|
||||
|
||||
// Determine if an email can be added to a given conversation given the folder
|
||||
canJoinThread: function (account, emailFolderId, convFolderId) {
|
||||
var draftsFolderId = account.getDraftsFolderId();
|
||||
var outboxFolderId = account.getOutboxFolderId();
|
||||
|
||||
// Never thread anything in the drafts or outbox folders
|
||||
if (emailFolderId === draftsFolderId || emailFolderId === outboxFolderId ||
|
||||
convFolderId === draftsFolderId || convFolderId === outboxFolderId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
var trashFolderId = account.getTrashFolderId();
|
||||
var archiveFolderId = account.getArchiveFolderId();
|
||||
|
||||
// Trash and archive folders can only join threads in the same folder
|
||||
if (emailFolderId !== convFolderId &&
|
||||
(emailFolderId === trashFolderId || emailFolderId === archiveFolderId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
@@ -396,6 +407,7 @@ enyo.kind({
|
||||
var conversation = null;
|
||||
|
||||
var accountId = enyo.application.folderProcessor.getFolderAccount(email.folderId);
|
||||
var account = enyo.application.accounts.getAccount(accountId);
|
||||
|
||||
var convResults;
|
||||
|
||||
@@ -405,30 +417,67 @@ enyo.kind({
|
||||
convResults = this.convResultsByFolderId[email.folderId];
|
||||
}
|
||||
|
||||
if (convResults && this.shouldThreadEmail(email)) {
|
||||
var matches = [];
|
||||
|
||||
if (convResults) {
|
||||
if (email.serverConversationId) {
|
||||
if (!conversation && convResults.serverConversationIdHash[email.serverConversationId]) {
|
||||
conversation = convResults.serverConversationIdHash[email.serverConversationId];
|
||||
matches = convResults.serverConversationIdHash[email.serverConversationId];
|
||||
}
|
||||
} else {
|
||||
// Find by messageId
|
||||
if (!conversation && email.inReplyTo && convResults.messageIdHash[email.inReplyTo]) {
|
||||
conversation = convResults.messageIdHash[email.inReplyTo];
|
||||
matches = convResults.messageIdHash[email.inReplyTo];
|
||||
}
|
||||
|
||||
// Find by threadTopic (stripped subject)
|
||||
if (!conversation) {
|
||||
if (matches.length === 0) {
|
||||
var threadTopic = email.threadTopic || EmailProcessorUtils.stripSubject(email.subject);
|
||||
|
||||
if (threadTopic && convResults.threadTopicHash[threadTopic]) {
|
||||
conversation = convResults.threadTopicHash[threadTopic];
|
||||
matches = convResults.threadTopicHash[threadTopic];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matches.length > 0) {
|
||||
if (account && this.processor.useCrossFolderThreading()) {
|
||||
// Filter out conversations in trash/drafts/outbox/archive
|
||||
/*jshint loopfunc:true */
|
||||
matches = matches.filter(function (c) {
|
||||
return this.canJoinThread(account, email.folderId, c.getFolderId());
|
||||
}, this);
|
||||
}
|
||||
|
||||
// If there's multiple matches, find the one with the closest timestamp
|
||||
//
|
||||
// NOTE: at the moment, this won't prevent threads from getting combined
|
||||
// since it always picks at least one (which will get combined on the
|
||||
// next pass). This is useful only if we add some additional heuristics
|
||||
// like a date cutoff.
|
||||
var bestDelta = Number.MAX_VALUE;
|
||||
|
||||
for (var m = 0; m < matches.length; m += 1) {
|
||||
var match = matches[m];
|
||||
var delta = Math.abs(email.timestamp - match.timestamp);
|
||||
|
||||
if (!conversation || delta <= bestDelta) {
|
||||
conversation = matches[m];
|
||||
bestDelta = delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (conversation) {
|
||||
this.dirty[conversation.getId()] = conversation;
|
||||
|
||||
if (this.processor.useCrossFolderThreading()) {
|
||||
// HACK: switch primary folderId to inbox if adding an inbox email
|
||||
if (account && email.folderId === account.getInboxFolderId()) {
|
||||
conversation.setFolderId(folderId);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Create a new conversation
|
||||
conversation = new ConversationState();
|
||||
|
||||
Reference in new issue
Block a user