ListsFeed.sys.mjs (4511B)
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 const lazy = {}; 6 ChromeUtils.defineESModuleGetters(lazy, { 7 PersistentCache: "resource://newtab/lib/PersistentCache.sys.mjs", 8 }); 9 10 import { 11 actionTypes as at, 12 actionCreators as ac, 13 } from "resource://newtab/common/Actions.mjs"; 14 15 const PREF_LISTS_ENABLED = "widgets.lists.enabled"; 16 const PREF_SYSTEM_LISTS_ENABLED = "widgets.system.lists.enabled"; 17 const PREF_WIDGETS_LISTS_MAX_LISTS = "widgets.lists.maxLists"; 18 const CACHE_KEY = "lists_widget"; 19 20 /** 21 * Class for the Lists widget, which manages the updates to the lists widget 22 * and syncs with PersistentCache 23 */ 24 export class ListsFeed { 25 constructor() { 26 this.initialized = false; 27 this.cache = this.PersistentCache(CACHE_KEY, true); 28 } 29 30 get enabled() { 31 const prefs = this.store.getState()?.Prefs.values; 32 const nimbusListsEnabled = prefs.widgetsConfig?.listsEnabled; 33 const nimbusListsTrainhopEnabled = 34 prefs.trainhopConfig?.widgets?.listsEnabled; 35 36 return ( 37 prefs?.[PREF_LISTS_ENABLED] && 38 (prefs?.[PREF_SYSTEM_LISTS_ENABLED] || 39 nimbusListsEnabled || 40 nimbusListsTrainhopEnabled) 41 ); 42 } 43 44 async init() { 45 this.initialized = true; 46 await this.syncLists(true); 47 } 48 49 isOverMaximumListCount(lists) { 50 const prefs = this.store.getState()?.Prefs.values; 51 const maxListsCount = prefs?.[PREF_WIDGETS_LISTS_MAX_LISTS]; 52 const currentListsCount = Object.keys(lists).length; 53 return currentListsCount > maxListsCount; 54 } 55 56 async syncLists(isStartup = false) { 57 const cachedData = (await this.cache.get()) || {}; 58 const { lists, selected } = cachedData; 59 // only update lists if this has been set before 60 if (lists) { 61 // Ensure all lists have a `completed` array 62 for (const listId in lists) { 63 if (lists[listId]) { 64 const list = lists[listId]; 65 if (!Array.isArray(list.completed)) { 66 list.completed = []; 67 } 68 // move any completed tasks to the completed array 69 const activeTasks = []; 70 const completedTasks = []; 71 for (const task of list.tasks) { 72 if (task.completed === true) { 73 completedTasks.push(task); 74 } else { 75 activeTasks.push(task); 76 } 77 } 78 79 list.tasks = activeTasks; 80 list.completed = list.completed.concat(completedTasks); 81 } 82 } 83 84 // Bug 1981722 — Only trigger if the user has manually created more lists 85 // than allowed. Throwing here prevents lists from syncing. 86 if (this.isOverMaximumListCount(lists)) { 87 throw new Error(`Over the maximum list count`); 88 } 89 90 this.update({ lists }, isStartup); 91 } 92 93 if (selected) { 94 this.updateSelected(selected, isStartup); 95 } 96 } 97 98 update(data, isStartup = false) { 99 this.store.dispatch( 100 ac.BroadcastToContent({ 101 type: at.WIDGETS_LISTS_SET, 102 data: data.lists, 103 meta: isStartup, 104 }) 105 ); 106 } 107 108 updateSelected(data, isStartup = false) { 109 this.store.dispatch( 110 ac.BroadcastToContent({ 111 type: at.WIDGETS_LISTS_SET_SELECTED, 112 data, 113 meta: isStartup, 114 }) 115 ); 116 } 117 118 /** 119 * @param {object} action - The action object containing pref change data 120 * @param {string} action.data.name - The name of the pref that changed 121 */ 122 async onPrefChangedAction(action) { 123 switch (action.data.name) { 124 case PREF_LISTS_ENABLED: 125 case PREF_SYSTEM_LISTS_ENABLED: 126 case "trainhopConfig": 127 case "widgetsConfig": { 128 if (this.enabled && !this.initialized) { 129 await this.init(); 130 } 131 break; 132 } 133 } 134 } 135 136 async onAction(action) { 137 switch (action.type) { 138 case at.INIT: 139 if (this.enabled) { 140 await this.init(); 141 } 142 break; 143 case at.PREF_CHANGED: 144 await this.onPrefChangedAction(action); 145 break; 146 case at.WIDGETS_LISTS_UPDATE: 147 await this.cache.set("lists", action.data.lists); 148 this.update(action.data); 149 break; 150 case at.WIDGETS_LISTS_CHANGE_SELECTED: 151 await this.cache.set("selected", action.data); 152 this.updateSelected(action.data); 153 break; 154 } 155 } 156 } 157 158 ListsFeed.prototype.PersistentCache = (...args) => { 159 return new lazy.PersistentCache(...args); 160 };