Watch article history over SSE so the tab stays current while open.

This commit is contained in:
seb
2026-08-05 09:48:30 +02:00
parent 60e6858fd5
commit 44d69e541c
3 changed files with 75 additions and 4 deletions

View File

@@ -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');
};