forked from mozilla/activity-stream
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Store.jsm
150 lines (135 loc) · 5.08 KB
/
Store.jsm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/* 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] = function(...args) {
return this._store[method](...args);
}.bind(this);
});
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"];