Bug 1881588 - Add Wallpaper component r=home-newtab-reviewers,fluent-reviewers,bolsso...
[gecko.git] / browser / components / newtab / common / Actions.mjs
blob7273d80220fccc6240453b2894c43cfe689d87ee
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/. */
5 // This file is accessed from both content and system scopes.
7 export const MAIN_MESSAGE_TYPE = "ActivityStream:Main";
8 export const CONTENT_MESSAGE_TYPE = "ActivityStream:Content";
9 export const PRELOAD_MESSAGE_TYPE = "ActivityStream:PreloadedBrowser";
10 export const UI_CODE = 1;
11 export const BACKGROUND_PROCESS = 2;
13 /**
14  * globalImportContext - Are we in UI code (i.e. react, a dom) or some kind of background process?
15  *                       Use this in action creators if you need different logic
16  *                       for ui/background processes.
17  */
18 export const globalImportContext =
19   typeof Window === "undefined" ? BACKGROUND_PROCESS : UI_CODE;
21 // Create an object that avoids accidental differing key/value pairs:
22 // {
23 //   INIT: "INIT",
24 //   UNINIT: "UNINIT"
25 // }
26 export const actionTypes = {};
28 for (const type of [
29   "ABOUT_SPONSORED_TOP_SITES",
30   "ADDONS_INFO_REQUEST",
31   "ADDONS_INFO_RESPONSE",
32   "ARCHIVE_FROM_POCKET",
33   "AS_ROUTER_INITIALIZED",
34   "AS_ROUTER_PREF_CHANGED",
35   "AS_ROUTER_TARGETING_UPDATE",
36   "AS_ROUTER_TELEMETRY_USER_EVENT",
37   "BLOCK_URL",
38   "BOOKMARK_URL",
39   "CLEAR_PREF",
40   "COPY_DOWNLOAD_LINK",
41   "DELETE_BOOKMARK_BY_ID",
42   "DELETE_FROM_POCKET",
43   "DELETE_HISTORY_URL",
44   "DIALOG_CANCEL",
45   "DIALOG_OPEN",
46   "DISABLE_SEARCH",
47   "DISCOVERY_STREAM_COLLECTION_DISMISSIBLE_TOGGLE",
48   "DISCOVERY_STREAM_CONFIG_CHANGE",
49   "DISCOVERY_STREAM_CONFIG_RESET",
50   "DISCOVERY_STREAM_CONFIG_RESET_DEFAULTS",
51   "DISCOVERY_STREAM_CONFIG_SETUP",
52   "DISCOVERY_STREAM_CONFIG_SET_VALUE",
53   "DISCOVERY_STREAM_DEV_EXPIRE_CACHE",
54   "DISCOVERY_STREAM_DEV_IDLE_DAILY",
55   "DISCOVERY_STREAM_DEV_SYNC_RS",
56   "DISCOVERY_STREAM_DEV_SYSTEM_TICK",
57   "DISCOVERY_STREAM_EXPERIMENT_DATA",
58   "DISCOVERY_STREAM_FEEDS_UPDATE",
59   "DISCOVERY_STREAM_FEED_UPDATE",
60   "DISCOVERY_STREAM_IMPRESSION_STATS",
61   "DISCOVERY_STREAM_LAYOUT_RESET",
62   "DISCOVERY_STREAM_LAYOUT_UPDATE",
63   "DISCOVERY_STREAM_LINK_BLOCKED",
64   "DISCOVERY_STREAM_LOADED_CONTENT",
65   "DISCOVERY_STREAM_PERSONALIZATION_INIT",
66   "DISCOVERY_STREAM_PERSONALIZATION_LAST_UPDATED",
67   "DISCOVERY_STREAM_PERSONALIZATION_OVERRIDE",
68   "DISCOVERY_STREAM_PERSONALIZATION_RESET",
69   "DISCOVERY_STREAM_PERSONALIZATION_TOGGLE",
70   "DISCOVERY_STREAM_PERSONALIZATION_UPDATED",
71   "DISCOVERY_STREAM_POCKET_STATE_INIT",
72   "DISCOVERY_STREAM_POCKET_STATE_SET",
73   "DISCOVERY_STREAM_PREFS_SETUP",
74   "DISCOVERY_STREAM_RECENT_SAVES",
75   "DISCOVERY_STREAM_RETRY_FEED",
76   "DISCOVERY_STREAM_SPOCS_CAPS",
77   "DISCOVERY_STREAM_SPOCS_ENDPOINT",
78   "DISCOVERY_STREAM_SPOCS_PLACEMENTS",
79   "DISCOVERY_STREAM_SPOCS_UPDATE",
80   "DISCOVERY_STREAM_SPOC_BLOCKED",
81   "DISCOVERY_STREAM_SPOC_IMPRESSION",
82   "DISCOVERY_STREAM_USER_EVENT",
83   "DOWNLOAD_CHANGED",
84   "FAKE_FOCUS_SEARCH",
85   "FILL_SEARCH_TERM",
86   "HANDOFF_SEARCH_TO_AWESOMEBAR",
87   "HIDE_PERSONALIZE",
88   "HIDE_PRIVACY_INFO",
89   "INIT",
90   "NEW_TAB_INIT",
91   "NEW_TAB_INITIAL_STATE",
92   "NEW_TAB_LOAD",
93   "NEW_TAB_REHYDRATED",
94   "NEW_TAB_STATE_REQUEST",
95   "NEW_TAB_UNLOAD",
96   "OPEN_DOWNLOAD_FILE",
97   "OPEN_LINK",
98   "OPEN_NEW_WINDOW",
99   "OPEN_PRIVATE_WINDOW",
100   "OPEN_WEBEXT_SETTINGS",
101   "PARTNER_LINK_ATTRIBUTION",
102   "PLACES_BOOKMARKS_REMOVED",
103   "PLACES_BOOKMARK_ADDED",
104   "PLACES_HISTORY_CLEARED",
105   "PLACES_LINKS_CHANGED",
106   "PLACES_LINKS_DELETED",
107   "PLACES_LINK_BLOCKED",
108   "PLACES_SAVED_TO_POCKET",
109   "POCKET_CTA",
110   "POCKET_LINK_DELETED_OR_ARCHIVED",
111   "POCKET_LOGGED_IN",
112   "POCKET_WAITING_FOR_SPOC",
113   "PREFS_INITIAL_VALUES",
114   "PREF_CHANGED",
115   "PREVIEW_REQUEST",
116   "PREVIEW_REQUEST_CANCEL",
117   "PREVIEW_RESPONSE",
118   "REMOVE_DOWNLOAD_FILE",
119   "RICH_ICON_MISSING",
120   "SAVE_SESSION_PERF_DATA",
121   "SAVE_TO_POCKET",
122   "SCREENSHOT_UPDATED",
123   "SECTION_DEREGISTER",
124   "SECTION_DISABLE",
125   "SECTION_ENABLE",
126   "SECTION_MOVE",
127   "SECTION_OPTIONS_CHANGED",
128   "SECTION_REGISTER",
129   "SECTION_UPDATE",
130   "SECTION_UPDATE_CARD",
131   "SETTINGS_CLOSE",
132   "SETTINGS_OPEN",
133   "SET_PREF",
134   "SHOW_DOWNLOAD_FILE",
135   "SHOW_FIREFOX_ACCOUNTS",
136   "SHOW_PERSONALIZE",
137   "SHOW_PRIVACY_INFO",
138   "SHOW_SEARCH",
139   "SKIPPED_SIGNIN",
140   "SOV_UPDATED",
141   "SUBMIT_EMAIL",
142   "SUBMIT_SIGNIN",
143   "SYSTEM_TICK",
144   "TELEMETRY_IMPRESSION_STATS",
145   "TELEMETRY_USER_EVENT",
146   "TOP_SITES_CANCEL_EDIT",
147   "TOP_SITES_CLOSE_SEARCH_SHORTCUTS_MODAL",
148   "TOP_SITES_EDIT",
149   "TOP_SITES_INSERT",
150   "TOP_SITES_OPEN_SEARCH_SHORTCUTS_MODAL",
151   "TOP_SITES_ORGANIC_IMPRESSION_STATS",
152   "TOP_SITES_PIN",
153   "TOP_SITES_PREFS_UPDATED",
154   "TOP_SITES_SPONSORED_IMPRESSION_STATS",
155   "TOP_SITES_UNPIN",
156   "TOP_SITES_UPDATED",
157   "TOTAL_BOOKMARKS_REQUEST",
158   "TOTAL_BOOKMARKS_RESPONSE",
159   "UNINIT",
160   "UPDATE_PINNED_SEARCH_SHORTCUTS",
161   "UPDATE_SEARCH_SHORTCUTS",
162   "UPDATE_SECTION_PREFS",
163   "WALLPAPERS_SET",
164   "WEBEXT_CLICK",
165   "WEBEXT_DISMISS",
166 ]) {
167   actionTypes[type] = type;
170 // Helper function for creating routed actions between content and main
171 // Not intended to be used by consumers
172 function _RouteMessage(action, options) {
173   const meta = action.meta ? { ...action.meta } : {};
174   if (!options || !options.from || !options.to) {
175     throw new Error(
176       "Routed Messages must have options as the second parameter, and must at least include a .from and .to property."
177     );
178   }
179   // For each of these fields, if they are passed as an option,
180   // add them to the action. If they are not defined, remove them.
181   ["from", "to", "toTarget", "fromTarget", "skipMain", "skipLocal"].forEach(
182     o => {
183       if (typeof options[o] !== "undefined") {
184         meta[o] = options[o];
185       } else if (meta[o]) {
186         delete meta[o];
187       }
188     }
189   );
190   return { ...action, meta };
194  * AlsoToMain - Creates a message that will be dispatched locally and also sent to the Main process.
196  * @param  {object} action Any redux action (required)
197  * @param  {object} options
198  * @param  {bool}   skipLocal Used by OnlyToMain to skip the main reducer
199  * @param  {string} fromTarget The id of the content port from which the action originated. (optional)
200  * @return {object} An action with added .meta properties
201  */
202 function AlsoToMain(action, fromTarget, skipLocal) {
203   return _RouteMessage(action, {
204     from: CONTENT_MESSAGE_TYPE,
205     to: MAIN_MESSAGE_TYPE,
206     fromTarget,
207     skipLocal,
208   });
212  * OnlyToMain - Creates a message that will be sent to the Main process and skip the local reducer.
214  * @param  {object} action Any redux action (required)
215  * @param  {object} options
216  * @param  {string} fromTarget The id of the content port from which the action originated. (optional)
217  * @return {object} An action with added .meta properties
218  */
219 function OnlyToMain(action, fromTarget) {
220   return AlsoToMain(action, fromTarget, true);
224  * BroadcastToContent - Creates a message that will be dispatched to main and sent to ALL content processes.
226  * @param  {object} action Any redux action (required)
227  * @return {object} An action with added .meta properties
228  */
229 function BroadcastToContent(action) {
230   return _RouteMessage(action, {
231     from: MAIN_MESSAGE_TYPE,
232     to: CONTENT_MESSAGE_TYPE,
233   });
237  * AlsoToOneContent - Creates a message that will be will be dispatched to the main store
238  *                    and also sent to a particular Content process.
240  * @param  {object} action Any redux action (required)
241  * @param  {string} target The id of a content port
242  * @param  {bool} skipMain Used by OnlyToOneContent to skip the main process
243  * @return {object} An action with added .meta properties
244  */
245 function AlsoToOneContent(action, target, skipMain) {
246   if (!target) {
247     throw new Error(
248       "You must provide a target ID as the second parameter of AlsoToOneContent. If you want to send to all content processes, use BroadcastToContent"
249     );
250   }
251   return _RouteMessage(action, {
252     from: MAIN_MESSAGE_TYPE,
253     to: CONTENT_MESSAGE_TYPE,
254     toTarget: target,
255     skipMain,
256   });
260  * OnlyToOneContent - Creates a message that will be sent to a particular Content process
261  *                    and skip the main reducer.
263  * @param  {object} action Any redux action (required)
264  * @param  {string} target The id of a content port
265  * @return {object} An action with added .meta properties
266  */
267 function OnlyToOneContent(action, target) {
268   return AlsoToOneContent(action, target, true);
272  * AlsoToPreloaded - Creates a message that dispatched to the main reducer and also sent to the preloaded tab.
274  * @param  {object} action Any redux action (required)
275  * @return {object} An action with added .meta properties
276  */
277 function AlsoToPreloaded(action) {
278   return _RouteMessage(action, {
279     from: MAIN_MESSAGE_TYPE,
280     to: PRELOAD_MESSAGE_TYPE,
281   });
285  * UserEvent - A telemetry ping indicating a user action. This should only
286  *                   be sent from the UI during a user session.
288  * @param  {object} data Fields to include in the ping (source, etc.)
289  * @return {object} An AlsoToMain action
290  */
291 function UserEvent(data) {
292   return AlsoToMain({
293     type: actionTypes.TELEMETRY_USER_EVENT,
294     data,
295   });
299  * DiscoveryStreamUserEvent - A telemetry ping indicating a user action from Discovery Stream. This should only
300  *                     be sent from the UI during a user session.
302  * @param  {object} data Fields to include in the ping (source, etc.)
303  * @return {object} An AlsoToMain action
304  */
305 function DiscoveryStreamUserEvent(data) {
306   return AlsoToMain({
307     type: actionTypes.DISCOVERY_STREAM_USER_EVENT,
308     data,
309   });
313  * ASRouterUserEvent - A telemetry ping indicating a user action from AS router. This should only
314  *                     be sent from the UI during a user session.
316  * @param  {object} data Fields to include in the ping (source, etc.)
317  * @return {object} An AlsoToMain action
318  */
319 function ASRouterUserEvent(data) {
320   return AlsoToMain({
321     type: actionTypes.AS_ROUTER_TELEMETRY_USER_EVENT,
322     data,
323   });
327  * ImpressionStats - A telemetry ping indicating an impression stats.
329  * @param  {object} data Fields to include in the ping
330  * @param  {int} importContext (For testing) Override the import context for testing.
331  * #return {object} An action. For UI code, a AlsoToMain action.
332  */
333 function ImpressionStats(data, importContext = globalImportContext) {
334   const action = {
335     type: actionTypes.TELEMETRY_IMPRESSION_STATS,
336     data,
337   };
338   return importContext === UI_CODE ? AlsoToMain(action) : action;
342  * DiscoveryStreamImpressionStats - A telemetry ping indicating an impression stats in Discovery Stream.
344  * @param  {object} data Fields to include in the ping
345  * @param  {int} importContext (For testing) Override the import context for testing.
346  * #return {object} An action. For UI code, a AlsoToMain action.
347  */
348 function DiscoveryStreamImpressionStats(
349   data,
350   importContext = globalImportContext
351 ) {
352   const action = {
353     type: actionTypes.DISCOVERY_STREAM_IMPRESSION_STATS,
354     data,
355   };
356   return importContext === UI_CODE ? AlsoToMain(action) : action;
360  * DiscoveryStreamLoadedContent - A telemetry ping indicating a content gets loaded in Discovery Stream.
362  * @param  {object} data Fields to include in the ping
363  * @param  {int} importContext (For testing) Override the import context for testing.
364  * #return {object} An action. For UI code, a AlsoToMain action.
365  */
366 function DiscoveryStreamLoadedContent(
367   data,
368   importContext = globalImportContext
369 ) {
370   const action = {
371     type: actionTypes.DISCOVERY_STREAM_LOADED_CONTENT,
372     data,
373   };
374   return importContext === UI_CODE ? AlsoToMain(action) : action;
377 function SetPref(prefName, value, importContext = globalImportContext) {
378   const action = {
379     type: actionTypes.SET_PREF,
380     data: { name: prefName, value },
381   };
382   return importContext === UI_CODE ? AlsoToMain(action) : action;
385 function WebExtEvent(type, data, importContext = globalImportContext) {
386   if (!data || !data.source) {
387     throw new Error(
388       'WebExtEvent actions should include a property "source", the id of the webextension that should receive the event.'
389     );
390   }
391   const action = { type, data };
392   return importContext === UI_CODE ? AlsoToMain(action) : action;
395 export const actionCreators = {
396   BroadcastToContent,
397   UserEvent,
398   DiscoveryStreamUserEvent,
399   ASRouterUserEvent,
400   ImpressionStats,
401   AlsoToOneContent,
402   OnlyToOneContent,
403   AlsoToMain,
404   OnlyToMain,
405   AlsoToPreloaded,
406   SetPref,
407   WebExtEvent,
408   DiscoveryStreamImpressionStats,
409   DiscoveryStreamLoadedContent,
412 // These are helpers to test for certain kinds of actions
413 export const actionUtils = {
414   isSendToMain(action) {
415     if (!action.meta) {
416       return false;
417     }
418     return (
419       action.meta.to === MAIN_MESSAGE_TYPE &&
420       action.meta.from === CONTENT_MESSAGE_TYPE
421     );
422   },
423   isBroadcastToContent(action) {
424     if (!action.meta) {
425       return false;
426     }
427     if (action.meta.to === CONTENT_MESSAGE_TYPE && !action.meta.toTarget) {
428       return true;
429     }
430     return false;
431   },
432   isSendToOneContent(action) {
433     if (!action.meta) {
434       return false;
435     }
436     if (action.meta.to === CONTENT_MESSAGE_TYPE && action.meta.toTarget) {
437       return true;
438     }
439     return false;
440   },
441   isSendToPreloaded(action) {
442     if (!action.meta) {
443       return false;
444     }
445     return (
446       action.meta.to === PRELOAD_MESSAGE_TYPE &&
447       action.meta.from === MAIN_MESSAGE_TYPE
448     );
449   },
450   isFromMain(action) {
451     if (!action.meta) {
452       return false;
453     }
454     return (
455       action.meta.from === MAIN_MESSAGE_TYPE &&
456       action.meta.to === CONTENT_MESSAGE_TYPE
457     );
458   },
459   getPortIdOfSender(action) {
460     return (action.meta && action.meta.fromTarget) || null;
461   },
462   _RouteMessage,