Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* This file provides the "explicit attribute" provider for messages. It is
* concerned with attributes that are the result of user actions. For example,
* whether a message is starred (flagged), message tags, whether it is
* read/unread, etc.
*/
import { Gloda } from "resource:///modules/gloda/Gloda.sys.mjs";
import { GlodaConstants } from "resource:///modules/gloda/GlodaConstants.sys.mjs";
import { TagNoun } from "resource:///modules/gloda/NounTag.sys.mjs";
/**
* @namespace Explicit attribute provider. Indexes/defines attributes that are
* explicitly a result of user action. This dubiously includes marking a
* message as read.
*/
export var GlodaExplicitAttr = {
providerName: "gloda.explattr",
strings: Services.strings.createBundle(
),
_log: null,
init() {
this._log = console.createInstance({
prefix: "gloda.explattr",
maxLogLevel: "Warn",
maxLogLevelPref: "gloda.loglevel",
});
try {
this.defineAttributes();
} catch (ex) {
this._log.error("Error in init: " + ex);
throw ex;
}
},
/** Boost for starred messages. */
NOTABILITY_STARRED: 16,
/** Boost for tagged messages, first tag. */
NOTABILITY_TAGGED_FIRST: 8,
/** Boost for tagged messages, each additional tag. */
NOTABILITY_TAGGED_ADDL: 1,
defineAttributes() {
// Tag
this._attrTag = Gloda.defineAttribute({
provider: this,
extensionName: GlodaConstants.BUILT_IN,
attributeType: GlodaConstants.kAttrExplicit,
attributeName: "tag",
bindName: "tags",
singular: false,
emptySetIsSignificant: true,
facet: true,
subjectNouns: [GlodaConstants.NOUN_MESSAGE],
objectNoun: GlodaConstants.NOUN_TAG,
parameterNoun: null,
// Property change notifications that we care about:
propertyChanges: ["keywords"],
}); // not-tested
// Star
this._attrStar = Gloda.defineAttribute({
provider: this,
extensionName: GlodaConstants.BUILT_IN,
attributeType: GlodaConstants.kAttrExplicit,
attributeName: "star",
bindName: "starred",
singular: true,
facet: true,
subjectNouns: [GlodaConstants.NOUN_MESSAGE],
objectNoun: GlodaConstants.NOUN_BOOLEAN,
parameterNoun: null,
}); // tested-by: test_attributes_explicit
// Read/Unread
this._attrRead = Gloda.defineAttribute({
provider: this,
extensionName: GlodaConstants.BUILT_IN,
attributeType: GlodaConstants.kAttrExplicit,
attributeName: "read",
// Make the message query-able but without using the database.
canQuery: "truthy-but-not-true",
singular: true,
subjectNouns: [GlodaConstants.NOUN_MESSAGE],
objectNoun: GlodaConstants.NOUN_BOOLEAN,
parameterNoun: null,
}); // tested-by: test_attributes_explicit
/**
* Has this message been replied to by the user.
*/
this._attrRepliedTo = Gloda.defineAttribute({
provider: this,
extensionName: GlodaConstants.BUILT_IN,
attributeType: GlodaConstants.kAttrExplicit,
attributeName: "repliedTo",
singular: true,
subjectNouns: [GlodaConstants.NOUN_MESSAGE],
objectNoun: GlodaConstants.NOUN_BOOLEAN,
parameterNoun: null,
}); // tested-by: test_attributes_explicit
/**
* Has this user forwarded this message to someone.
*/
this._attrForwarded = Gloda.defineAttribute({
provider: this,
extensionName: GlodaConstants.BUILT_IN,
attributeType: GlodaConstants.kAttrExplicit,
attributeName: "forwarded",
singular: true,
subjectNouns: [GlodaConstants.NOUN_MESSAGE],
objectNoun: GlodaConstants.NOUN_BOOLEAN,
parameterNoun: null,
}); // tested-by: test_attributes_explicit
},
*process(aGlodaMessage, aRawReps) {
const aMsgHdr = aRawReps.header;
aGlodaMessage.starred = aMsgHdr.isFlagged;
if (aGlodaMessage.starred) {
aGlodaMessage.notability += this.NOTABILITY_STARRED;
}
aGlodaMessage.read = aMsgHdr.isRead;
const flags = aMsgHdr.flags;
aGlodaMessage.repliedTo = Boolean(flags & Ci.nsMsgMessageFlags.Replied);
aGlodaMessage.forwarded = Boolean(flags & Ci.nsMsgMessageFlags.Forwarded);
const tags = (aGlodaMessage.tags = []);
// -- Tag
// build a map of the keywords
const keywords = aMsgHdr.getStringProperty("keywords");
const keywordList = keywords.split(" ");
const keywordMap = {};
for (let iKeyword = 0; iKeyword < keywordList.length; iKeyword++) {
const keyword = keywordList[iKeyword];
keywordMap[keyword] = true;
}
const tagArray = TagNoun.getAllTags();
for (let iTag = 0; iTag < tagArray.length; iTag++) {
const tag = tagArray[iTag];
if (tag.key in keywordMap) {
tags.push(tag);
}
}
if (tags.length) {
aGlodaMessage.notability +=
this.NOTABILITY_TAGGED_FIRST +
(tags.length - 1) * this.NOTABILITY_TAGGED_ADDL;
}
yield GlodaConstants.kWorkDone;
},
/**
* Duplicates the notability logic from process(). Arguably process should
* be factored to call us, grokNounItem should be factored to call us, or we
* should get sufficiently fancy that our code wildly diverges.
*/
score(aMessage) {
let score = 0;
if (aMessage.starred) {
score += this.NOTABILITY_STARRED;
}
if (aMessage.tags.length) {
score +=
this.NOTABILITY_TAGGED_FIRST +
(aMessage.tags.length - 1) * this.NOTABILITY_TAGGED_ADDL;
}
return score;
},
};