Skip to main content

max / balanced_breakfast

2.7 KB · 91 lines History Blame Raw
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 // Proxy for direct access: BB.state.sources etc.
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