| 1 |
|
| 2 |
* @fileoverview Centralized reactive state store with pub/sub. |
| 3 |
* |
| 4 |
* Wraps a plain data object in a Proxy so that `BB.state.foo = bar` triggers |
| 5 |
* subscribers. The Proxy approach (vs. explicit getters/setters) keeps the API |
| 6 |
* concise: any property access reads directly from `data`, any assignment calls |
| 7 |
* `set()` which notifies listeners. |
| 8 |
|
| 9 |
(function() { |
| 10 |
'use strict'; |
| 11 |
|
| 12 |
const subscribers = {}; |
| 13 |
const data = { |
| 14 |
sources: [], |
| 15 |
items: [], |
| 16 |
currentSource: '', |
| 17 |
currentOrder: 'chronological', |
| 18 |
currentSearch: '', |
| 19 |
currentPage: 0, |
| 20 |
hasMore: false, |
| 21 |
selectedItemId: null, |
| 22 |
selectedPluginId: null, |
| 23 |
allTags: [], |
| 24 |
currentTag: '', |
| 25 |
queryFeeds: [], |
| 26 |
currentQueryFeed: null, |
| 27 |
sessionArticlesRead: 0, |
| 28 |
sessionArticlesStarred: 0, |
| 29 |
}; |
| 30 |
|
| 31 |
|
| 32 |
* Subscribe to changes on a state key. Returns an unsubscribe function. |
| 33 |
* @param {string} key - State property name to watch. |
| 34 |
* @param {function(any, any): void} callback - Called with (newValue, oldValue). |
| 35 |
* @returns {function(): void} Unsubscribe function. |
| 36 |
|
| 37 |
function subscribe(key, callback) { |
| 38 |
if (!subscribers[key]) subscribers[key] = []; |
| 39 |
subscribers[key].push(callback); |
| 40 |
return () => { |
| 41 |
subscribers[key] = subscribers[key].filter(cb => cb !== callback); |
| 42 |
}; |
| 43 |
} |
| 44 |
|
| 45 |
|
| 46 |
* Update a state value and notify all subscribers for that key. |
| 47 |
* @param {string} key - State property name. |
| 48 |
* @param {any} value - New value. |
| 49 |
|
| 50 |
function set(key, value) { |
| 51 |
const old = data[key]; |
| 52 |
data[key] = value; |
| 53 |
if (subscribers[key]) { |
| 54 |
subscribers[key].forEach(cb => cb(value, old)); |
| 55 |
} |
| 56 |
} |
| 57 |
|
| 58 |
|
| 59 |
* Read a state value. |
| 60 |
* @param {string} key - State property name. |
| 61 |
* @returns {any} Current value. |
| 62 |
|
| 63 |
function get(key) { |
| 64 |
return data[key]; |
| 65 |
} |
| 66 |
|
| 67 |
|
| 68 |
* Reset pagination to the first page, optionally clearing selection. |
| 69 |
* @param {boolean} [clearSelection=false] - Also clear selectedItemId. |
| 70 |
|
| 71 |
function resetPagination(clearSelection) { |
| 72 |
set('currentPage', 0); |
| 73 |
if (clearSelection) set('selectedItemId', null); |
| 74 |
} |
| 75 |
|
| 76 |
|
| 77 |
BB.state = new Proxy(data, { |
| 78 |
get(target, prop) { |
| 79 |
if (prop === 'subscribe') return subscribe; |
| 80 |
if (prop === 'set') return set; |
| 81 |
if (prop === 'get') return get; |
| 82 |
if (prop === 'resetPagination') return resetPagination; |
| 83 |
return target[prop]; |
| 84 |
}, |
| 85 |
set(target, prop, value) { |
| 86 |
set(prop, value); |
| 87 |
return true; |
| 88 |
}, |
| 89 |
}); |
| 90 |
})(); |
| 91 |
|