diff options
Diffstat (limited to 'comm/mail/components/activity/content/activity.js')
-rw-r--r-- | comm/mail/components/activity/content/activity.js | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/comm/mail/components/activity/content/activity.js b/comm/mail/components/activity/content/activity.js new file mode 100644 index 0000000000..dcaba3d808 --- /dev/null +++ b/comm/mail/components/activity/content/activity.js @@ -0,0 +1,239 @@ +/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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/. */ + +const activityManager = Cc["@mozilla.org/activity-manager;1"].getService( + Ci.nsIActivityManager +); + +var ACTIVITY_LIMIT = 250; + +var activityObject = { + _activityMgrListener: null, + _activitiesView: null, + _activityLogger: console.createInstance({ + prefix: "mail.activity", + maxLogLevel: "Warn", + maxLogLevelPref: "mail.activity.loglevel", + }), + _ignoreNotifications: false, + _groupCache: new Map(), + + // Utility Functions for Activity element management + + /** + * Creates the proper element for the given activity + */ + createActivityWidget(type) { + let element = document.createElement("li", { + is: type.bindingName, + }); + + if (element) { + element.setAttribute("actID", type.id); + } + + return element; + }, + + /** + * Returns the activity group element that matches the context_type + * and context of the given activity, if any. + */ + getActivityGroupElementByContext(aContextType, aContextObj) { + return this._groupCache.get(aContextType + ":" + aContextObj); + }, + + /** + * Inserts the given element into the correct position on the + * activity manager window. + */ + placeActivityElement(element) { + if (element.isGroup || element.isProcess) { + this._activitiesView.insertBefore( + element, + this._activitiesView.firstElementChild + ); + } else { + let next = this._activitiesView.firstElementChild; + while (next && (next.isWarning || next.isProcess || next.isGroup)) { + next = next.nextElementSibling; + } + if (next) { + this._activitiesView.insertBefore(element, next); + } else { + this._activitiesView.appendChild(element); + } + } + if (element.isGroup) { + this._groupCache.set( + element.contextType + ":" + element.contextObj, + element + ); + } + while (this._activitiesView.children.length > ACTIVITY_LIMIT) { + this.removeActivityElement( + this._activitiesView.lastElementChild.getAttribute("actID") + ); + } + }, + + /** + * Adds a new element to activity manager window for the + * given activity. It is called by ActivityMgrListener when + * a new activity is added into the activity manager's internal + * list. + */ + addActivityElement(aID, aActivity) { + try { + this._activityLogger.info(`Adding ActivityElement: ${aID}, ${aActivity}`); + // get |groupingStyle| of the activity. Grouping style determines + // whether we show the activity standalone or grouped by context in + // the activity manager window. + let isGroupByContext = + aActivity.groupingStyle == Ci.nsIActivity.GROUPING_STYLE_BYCONTEXT; + + // find out if an activity group has already been created for this context + let group = null; + if (isGroupByContext) { + group = this.getActivityGroupElementByContext( + aActivity.contextType, + aActivity.contextObj + ); + // create a group if it's not already created. + if (!group) { + group = document.createElement("li", { + is: "activity-group-item", + }); + this._activityLogger.info("created group element"); + // Set the context type and object of the newly created group + group.contextType = aActivity.contextType; + group.contextObj = aActivity.contextObj; + group.contextDisplayText = aActivity.contextDisplayText; + + // add group into the list + this.placeActivityElement(group); + } + } + + // create the appropriate element for the activity + let actElement = this.createActivityWidget(aActivity); + this._activityLogger.info("created activity element"); + + if (group) { + // get the inner list element of the group + let groupView = group.querySelector(".activitygroup-list"); + groupView.appendChild(actElement); + } else { + this.placeActivityElement(actElement); + } + } catch (e) { + this._activityLogger.error("addActivityElement: " + e); + throw e; + } + }, + + /** + * Removes the activity element from the activity manager window. + * It is called by ActivityMgrListener when the activity in question + * is removed from the activity manager's internal list. + */ + removeActivityElement(aID) { + this._activityLogger.info("removing Activity ID: " + aID); + let item = this._activitiesView.querySelector(`[actID="${aID}"]`); + + if (item) { + let group = item.closest(".activitygroup"); + item.remove(); + if (group && !group.querySelector(".activityitem")) { + // Empty group is removed. + this._groupCache.delete(group.contextType + ":" + group.contextObj); + group.remove(); + } + } + }, + + // ----------------- + // Startup, Shutdown + + startup() { + try { + this._activitiesView = document.getElementById("activityView"); + + let activities = activityManager.getActivities(); + for ( + let iActivity = Math.max(0, activities.length - ACTIVITY_LIMIT); + iActivity < activities.length; + iActivity++ + ) { + let activity = activities[iActivity]; + this.addActivityElement(activity.id, activity); + } + + // start listening changes in the activity manager's + // internal list + this._activityMgrListener = new this.ActivityMgrListener(); + activityManager.addListener(this._activityMgrListener); + } catch (e) { + this._activityLogger.error("Exception: " + e); + } + }, + + rebuild() { + let activities = activityManager.getActivities(); + for (let activity of activities) { + this.addActivityElement(activity.id, activity); + } + }, + + shutdown() { + activityManager.removeListener(this._activityMgrListener); + }, + + // ----------------- + // Utility Functions + + /** + * Remove all activities not in-progress from the activity list. + */ + clearActivityList() { + this._activityLogger.debug("clearActivityList"); + + this._ignoreNotifications = true; + // If/when we implement search, we'll want to remove just the items + // that are on the search display, however for now, we'll just clear up + // everything. + activityManager.cleanUp(); + + while (this._activitiesView.lastChild) { + this._activitiesView.lastChild.remove(); + } + + this._groupCache.clear(); + this.rebuild(); + this._ignoreNotifications = false; + this._activitiesView.focus(); + }, +}; + +// An object to monitor nsActivityManager operations. This class acts as +// binding layer between nsActivityManager and nsActivityManagerUI objects. +activityObject.ActivityMgrListener = function () {}; +activityObject.ActivityMgrListener.prototype = { + onAddedActivity(aID, aActivity) { + activityObject._activityLogger.info(`added activity: ${aID} ${aActivity}`); + if (!activityObject._ignoreNotifications) { + activityObject.addActivityElement(aID, aActivity); + } + }, + + onRemovedActivity(aID) { + if (!activityObject._ignoreNotifications) { + activityObject.removeActivityElement(aID); + } + }, +}; + +window.addEventListener("load", () => activityObject.startup()); +window.addEventListener("unload", () => activityObject.shutdown()); |