Store.sys.mjs (5447B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 import { ActivityStreamMessageChannel } from "resource://newtab/lib/ActivityStreamMessageChannel.sys.mjs"; 6 import { Prefs } from "resource://newtab/lib/ActivityStreamPrefs.sys.mjs"; 7 import { reducers } from "resource://newtab/common/Reducers.sys.mjs"; 8 import { redux } from "chrome://global/content/vendor/Redux.sys.mjs"; 9 10 /** 11 * Store - This has a similar structure to a redux store, but includes some extra 12 * functionality to allow for routing of actions between the Main processes 13 * and child processes via a ActivityStreamMessageChannel. 14 * It also accepts an array of "Feeds" on inititalization, which 15 * can listen for any action that is dispatched through the store. 16 */ 17 export class Store { 18 /** 19 * constructor - The redux store and message manager are created here, 20 * but no listeners are added until "init" is called. 21 */ 22 constructor() { 23 this._middleware = this._middleware.bind(this); 24 // Bind each redux method so we can call it directly from the Store. E.g., 25 // store.dispatch() will call store._store.dispatch(); 26 for (const method of ["dispatch", "getState", "subscribe"]) { 27 this[method] = (...args) => this._store[method](...args); 28 } 29 this.feeds = new Map(); 30 this._prefs = new Prefs(); 31 this._messageChannel = new ActivityStreamMessageChannel({ 32 dispatch: this.dispatch, 33 }); 34 this._store = redux.createStore( 35 redux.combineReducers(reducers), 36 redux.applyMiddleware(this._middleware, this._messageChannel.middleware) 37 ); 38 } 39 40 /** 41 * _middleware - This is redux middleware consumed by redux.createStore. 42 * it calls each feed's .onAction method, if one 43 * is defined. 44 */ 45 _middleware() { 46 return next => action => { 47 next(action); 48 for (const store of this.feeds.values()) { 49 if (store.onAction) { 50 store.onAction(action); 51 } 52 } 53 }; 54 } 55 56 /** 57 * initFeed - Initializes a feed by calling its constructor function 58 * 59 * @param {string} feedName The name of a feed, as defined in the object 60 * passed to Store.init 61 * @param {Action} initAction An optional action to initialize the feed 62 */ 63 initFeed(feedName, initAction) { 64 const feed = this._feedFactories.get(feedName)(); 65 feed.store = this; 66 this.feeds.set(feedName, feed); 67 if (initAction && feed.onAction) { 68 feed.onAction(initAction); 69 } 70 } 71 72 /** 73 * uninitFeed - Removes a feed and calls its uninit function if defined 74 * 75 * @param {string} feedName The name of a feed, as defined in the object 76 * passed to Store.init 77 * @param {Action} uninitAction An optional action to uninitialize the feed 78 */ 79 uninitFeed(feedName, uninitAction) { 80 const feed = this.feeds.get(feedName); 81 if (!feed) { 82 return; 83 } 84 if (uninitAction && feed.onAction) { 85 feed.onAction(uninitAction); 86 } 87 this.feeds.delete(feedName); 88 } 89 90 /** 91 * onPrefChanged - Listener for handling feed changes. 92 */ 93 onPrefChanged(name, value) { 94 if (this._feedFactories.has(name)) { 95 if (value) { 96 this.initFeed(name, this._initAction); 97 } else { 98 this.uninitFeed(name, this._uninitAction); 99 } 100 } 101 } 102 103 /** 104 * init - Initializes the ActivityStreamMessageChannel channel, and adds feeds. 105 * 106 * Note that it intentionally initializes the TelemetryFeed first so that the 107 * addon is able to report the init errors from other feeds. 108 * 109 * @param {Map} feedFactories A Map of feeds with the name of the pref for 110 * the feed as the key and a function that 111 * constructs an instance of the feed. 112 * @param {Action} initAction An optional action that will be dispatched 113 * to feeds when they're created. 114 * @param {Action} uninitAction An optional action for when feeds uninit. 115 */ 116 init(feedFactories, initAction, uninitAction) { 117 this._feedFactories = feedFactories; 118 this._initAction = initAction; 119 this._uninitAction = uninitAction; 120 121 const telemetryKey = "feeds.telemetry"; 122 if (feedFactories.has(telemetryKey) && this._prefs.get(telemetryKey)) { 123 this.initFeed(telemetryKey); 124 } 125 126 for (const pref of feedFactories.keys()) { 127 if (pref !== telemetryKey && this._prefs.get(pref)) { 128 this.initFeed(pref); 129 } 130 } 131 132 this._prefs.observeBranch(this); 133 134 // Dispatch an initial action after all enabled feeds are ready 135 if (initAction) { 136 this.dispatch(initAction); 137 } 138 139 // Dispatch NEW_TAB_INIT/NEW_TAB_LOAD events after INIT event. 140 this._messageChannel.simulateMessagesForExistingTabs(); 141 } 142 143 /** 144 * uninit - Uninitalizes each feed, clears them, and destroys the message 145 * manager channel. 146 */ 147 uninit() { 148 if (this._uninitAction) { 149 this.dispatch(this._uninitAction); 150 } 151 this._prefs.ignoreBranch(this); 152 this.feeds.clear(); 153 this._feedFactories = null; 154 } 155 156 /** 157 * getMessageChannel - Used by the AboutNewTabParent actor to get the message channel. 158 */ 159 getMessageChannel() { 160 return this._messageChannel; 161 } 162 }