151 lines
5.1 KiB
JavaScript
151 lines
5.1 KiB
JavaScript
/* 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/. */
|
|
/* global Preferences */
|
|
"use strict";
|
|
|
|
const {utils: Cu} = Components;
|
|
|
|
const {redux} = Cu.import("resource://activity-stream/vendor/Redux.jsm", {});
|
|
const {reducers} = Cu.import("resource://activity-stream/common/Reducers.jsm", {});
|
|
const {ActivityStreamMessageChannel} = Cu.import("resource://activity-stream/lib/ActivityStreamMessageChannel.jsm", {});
|
|
|
|
const PREF_PREFIX = "browser.newtabpage.activity-stream.";
|
|
Cu.import("resource://gre/modules/Preferences.jsm");
|
|
|
|
/**
|
|
* Store - This has a similar structure to a redux store, but includes some extra
|
|
* functionality to allow for routing of actions between the Main processes
|
|
* and child processes via a ActivityStreamMessageChannel.
|
|
* It also accepts an array of "Feeds" on inititalization, which
|
|
* can listen for any action that is dispatched through the store.
|
|
*/
|
|
this.Store = class Store {
|
|
|
|
/**
|
|
* constructor - The redux store and message manager are created here,
|
|
* but no listeners are added until "init" is called.
|
|
*/
|
|
constructor() {
|
|
this._middleware = this._middleware.bind(this);
|
|
// Bind each redux method so we can call it directly from the Store. E.g.,
|
|
// store.dispatch() will call store._store.dispatch();
|
|
["dispatch", "getState", "subscribe"].forEach(method => {
|
|
this[method] = (...args) => {
|
|
return this._store[method](...args);
|
|
};
|
|
});
|
|
this.feeds = new Map();
|
|
this._feedFactories = null;
|
|
this._prefHandlers = new Map();
|
|
this._messageChannel = new ActivityStreamMessageChannel({dispatch: this.dispatch});
|
|
this._store = redux.createStore(
|
|
redux.combineReducers(reducers),
|
|
redux.applyMiddleware(this._middleware, this._messageChannel.middleware)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* _middleware - This is redux middleware consumed by redux.createStore.
|
|
* it calls each feed's .onAction method, if one
|
|
* is defined.
|
|
*/
|
|
_middleware(store) {
|
|
return next => action => {
|
|
next(action);
|
|
this.feeds.forEach(s => s.onAction && s.onAction(action));
|
|
};
|
|
}
|
|
|
|
/**
|
|
* initFeed - Initializes a feed by calling its constructor function
|
|
*
|
|
* @param {string} feedName The name of a feed, as defined in the object
|
|
* passed to Store.init
|
|
*/
|
|
initFeed(feedName) {
|
|
const feed = this._feedFactories[feedName]();
|
|
feed.store = this;
|
|
this.feeds.set(feedName, feed);
|
|
}
|
|
|
|
/**
|
|
* uninitFeed - Removes a feed and calls its uninit function if defined
|
|
*
|
|
* @param {string} feedName The name of a feed, as defined in the object
|
|
* passed to Store.init
|
|
*/
|
|
uninitFeed(feedName) {
|
|
const feed = this.feeds.get(feedName);
|
|
if (!feed) {
|
|
return;
|
|
}
|
|
if (feed.uninit) {
|
|
feed.uninit();
|
|
}
|
|
this.feeds.delete(feedName);
|
|
}
|
|
|
|
/**
|
|
* maybeStartFeedAndListenForPrefChanges - Listen for pref changes that turn a
|
|
* feed off/on, and as long as that pref was not explicitly set to
|
|
* false, initialize the feed immediately.
|
|
*
|
|
* @param {string} name The name of a feed, as defined in the object passed
|
|
* to Store.init
|
|
*/
|
|
maybeStartFeedAndListenForPrefChanges(name) {
|
|
const prefName = PREF_PREFIX + name;
|
|
|
|
// If the pref was never set, set it to true by default.
|
|
if (!Preferences.has(prefName)) {
|
|
Preferences.set(prefName, true);
|
|
}
|
|
|
|
// Create a listener that turns the feed off/on based on changes
|
|
// to the pref, and cache it so we can unlisten on shut-down.
|
|
const onPrefChanged = isEnabled => (isEnabled ? this.initFeed(name) : this.uninitFeed(name));
|
|
this._prefHandlers.set(prefName, onPrefChanged);
|
|
Preferences.observe(prefName, onPrefChanged);
|
|
|
|
// TODO: This should propbably be done in a generic pref manager for Activity Stream.
|
|
// If the pref is true, start the feed immediately.
|
|
if (Preferences.get(prefName)) {
|
|
this.initFeed(name);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* init - Initializes the ActivityStreamMessageChannel channel, and adds feeds.
|
|
*
|
|
* @param {array} feeds An array of objects with an optional .onAction method
|
|
*/
|
|
init(feedConstructors) {
|
|
if (feedConstructors) {
|
|
this._feedFactories = feedConstructors;
|
|
for (const name of Object.keys(feedConstructors)) {
|
|
this.maybeStartFeedAndListenForPrefChanges(name);
|
|
}
|
|
}
|
|
this._messageChannel.createChannel();
|
|
}
|
|
|
|
/**
|
|
* uninit - Uninitalizes each feed, clears them, and destroys the message
|
|
* manager channel.
|
|
*
|
|
* @return {type} description
|
|
*/
|
|
uninit() {
|
|
this.feeds.forEach(feed => this.uninitFeed(feed));
|
|
this._prefHandlers.forEach((handler, pref) => Preferences.ignore(pref, handler));
|
|
this._prefHandlers.clear();
|
|
this._feedFactories = null;
|
|
this.feeds.clear();
|
|
this._messageChannel.destroyChannel();
|
|
}
|
|
};
|
|
|
|
this.PREF_PREFIX = PREF_PREFIX;
|
|
this.EXPORTED_SYMBOLS = ["Store", "PREF_PREFIX"];
|