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:
Jason Lai authored and Martin Jansa committed 2013-12-15 16:02:00 +01:00
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();