diff --git a/src/api/storageManager.jsx b/src/api/storageManager.jsx index 02e4363..7344499 100644 --- a/src/api/storageManager.jsx +++ b/src/api/storageManager.jsx @@ -53,6 +53,8 @@ function createCache(label, load) { class StorageManager { #baseUrl; + /** @type {EventSource | null} */ + #events = null; constructor(baseUrl = window.location.origin) { this.#baseUrl = baseUrl; @@ -215,7 +217,7 @@ class StorageManager { Object.defineProperties(this, { authMePromise: { get: () => authMeCache.promise }, updateAuthMe: { value: authMeCache.update }, - + articleIndexPromise: { get: () => articleIndexCache.promise }, updateArticleIndex: { value: articleIndexCache.update }, @@ -233,19 +235,63 @@ class StorageManager { posterminalPromise: { get: () => posterminalCache.promise }, updatePosterminal: { value: posterminalCache.update }, + + /** Close and reopen GET /api/events (required after selected-company change). */ + reconnectEvents: { value: () => this.#reconnectEvents() }, }); } + /** + * GET /api/events — SSE signals for the selected company. + * Stream is bound at connect time; reconnect after company switch. + */ + #reconnectEvents() { + this.#disconnectEvents(); + const url = buildUrl(this.#baseUrl, '/api/events'); + const es = new EventSource(url, { withCredentials: true }); + es.addEventListener('category', () => { + void this.updateCategoryTree().catch((err) => { + console.error('[storageMgr] category event refresh failed', err); + }); + }); + es.addEventListener('articles', () => { + void this.updateArticleIndex().catch((err) => { + console.error('[storageMgr] articles event refresh failed', err); + }); + }); + es.onerror = () => { + console.error('[storageMgr] events stream error'); + }; + this.#events = es; + } + + #disconnectEvents() { + if (!this.#events) return; + this.#events.close(); + this.#events = null; + } + + #syncEvents(user) { + if (user?.selectedCompanyId != null) { + this.#reconnectEvents(); + } else { + this.#disconnectEvents(); + } + } + async #loadAuthMe() { try { const data = await this.auth.me(); requireObject(data, 'GET /api/auth/me'); - return { + const session = { user: data.user, googleClientId: data.googleClientId, }; + this.#syncEvents(session.user); + return session; } catch (err) { if (err.status === 401 || err.status === 403) { + this.#syncEvents(null); return { user: null, googleClientId: err.data?.googleClientId,