Watch article history over SSE so the tab stays current while open.
This commit is contained in:
@@ -72,12 +72,67 @@ export class StorageManager extends ApiClient {
|
||||
#events = null;
|
||||
/** @type {ReturnType<typeof createCache> | null} */
|
||||
#articleIndexCache = null;
|
||||
/** @type {Map<string, Set<() => void>>} sku → refreshers */
|
||||
#historyWatchers = new Map();
|
||||
|
||||
constructor(baseUrl = window.location.origin) {
|
||||
super(baseUrl);
|
||||
this.#initCaches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch article history, then refetch whenever SSE emits `article-<sku>`.
|
||||
* @param {string} sku
|
||||
* @param {(history: unknown[]) => void} onHistory
|
||||
* @param {(err: unknown) => void} onError
|
||||
* @returns {() => void} unwatch
|
||||
*/
|
||||
watchArticleHistory(sku, onHistory, onError) {
|
||||
const key = String(sku);
|
||||
let active = true;
|
||||
|
||||
const refresh = () =>
|
||||
this.article.history(key).then((res) => {
|
||||
if (!active) return;
|
||||
requireObject(res, 'GET /api/article/{sku}/history');
|
||||
onHistory(requireArray(res.history, 'history'));
|
||||
});
|
||||
|
||||
const run = () =>
|
||||
void refresh().catch((err) => {
|
||||
if (!active) return;
|
||||
onError(err);
|
||||
});
|
||||
|
||||
let watchers = this.#historyWatchers.get(key);
|
||||
if (!watchers) {
|
||||
watchers = new Set();
|
||||
this.#historyWatchers.set(key, watchers);
|
||||
this.#events?.addEventListener(`article-${key}`, this.#onArticleHistorySignal);
|
||||
}
|
||||
watchers.add(run);
|
||||
|
||||
run();
|
||||
|
||||
return () => {
|
||||
active = false;
|
||||
const set = this.#historyWatchers.get(key);
|
||||
if (!set) return;
|
||||
set.delete(run);
|
||||
if (set.size === 0) {
|
||||
this.#historyWatchers.delete(key);
|
||||
this.#events?.removeEventListener(`article-${key}`, this.#onArticleHistorySignal);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#onArticleHistorySignal = (event) => {
|
||||
const sku = event.type.slice('article-'.length);
|
||||
const watchers = this.#historyWatchers.get(sku);
|
||||
if (!watchers) return;
|
||||
for (const run of watchers) run();
|
||||
};
|
||||
|
||||
/** Patch local articleIndex name if already loaded; no-op otherwise. */
|
||||
setArticleIndexName(sku, name) {
|
||||
const articles = this.#articleIndexCache.value;
|
||||
@@ -147,6 +202,9 @@ export class StorageManager extends ApiClient {
|
||||
console.error('[storageMgr] articles SSE refresh failed', err);
|
||||
});
|
||||
});
|
||||
for (const sku of this.#historyWatchers.keys()) {
|
||||
es.addEventListener(`article-${sku}`, this.#onArticleHistorySignal);
|
||||
}
|
||||
es.onerror = () => {
|
||||
console.error('[storageMgr] events stream error');
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user