u
This commit is contained in:
@@ -5,12 +5,19 @@
|
||||
* Upstream Triggers:
|
||||
* - `authAutomat`: Subscribes to auth state changes — calls `actions.load()` on
|
||||
* `'authed'` with a `selectedCompanyId`, and `actions.reset()` on `'anon'`.
|
||||
* - `sseAutomat`: Subscribes to the single `'brand'` event carrying
|
||||
* `{ sender, brandId }` — any create/rename/delete/picture change triggers
|
||||
* `actions.reload()`, which silently re-fetches the list. Subscribed views
|
||||
* (e.g. `ArtikelMarkenPage` via `brandAutomat.subscribe(...)`) then
|
||||
* re-render from the updated state. This is the reload pattern. Events sent
|
||||
* by this tab (matching `sender`) are ignored.
|
||||
* - UI / Settings: Mutating actions (`create`, `rename`, `remove`, `addImage`, `deleteImage`).
|
||||
*/
|
||||
|
||||
import { Automat } from 'react-automat';
|
||||
import { brand as brandApi } from '../api';
|
||||
import authAutomat from './authAutomat';
|
||||
import sseAutomat, { getSseClientId } from './sseAutomat';
|
||||
|
||||
/**
|
||||
* @typedef {Object} BrandState
|
||||
@@ -34,19 +41,56 @@ function sortBrands(brands) {
|
||||
|
||||
let brandAutomat;
|
||||
|
||||
/** In-flight `load()`/`reload()` promise so concurrent callers share one request. */
|
||||
let inflightLoad = null;
|
||||
|
||||
async function fetchBrands() {
|
||||
const res = await brandApi.list();
|
||||
return sortBrands(Array.isArray(res.brands) ? res.brands : []);
|
||||
}
|
||||
|
||||
const actions = {
|
||||
/** Load all brands for the selected company. */
|
||||
async load() {
|
||||
if (inflightLoad) return inflightLoad;
|
||||
brandAutomat.setState({ status: 'loading', error: null });
|
||||
try {
|
||||
const res = await brandApi.list();
|
||||
const brands = sortBrands(Array.isArray(res.brands) ? res.brands : []);
|
||||
brandAutomat.setState({ brands, status: 'succeeded' });
|
||||
return brands;
|
||||
} catch (err) {
|
||||
brandAutomat.setState({ status: 'failed', error: err?.message ?? String(err) });
|
||||
throw err;
|
||||
}
|
||||
inflightLoad = (async () => {
|
||||
try {
|
||||
const brands = await fetchBrands();
|
||||
brandAutomat.setState({ brands, status: 'succeeded' });
|
||||
return brands;
|
||||
} catch (err) {
|
||||
brandAutomat.setState({ status: 'failed', error: err?.message ?? String(err) });
|
||||
throw err;
|
||||
} finally {
|
||||
inflightLoad = null;
|
||||
}
|
||||
})();
|
||||
return inflightLoad;
|
||||
},
|
||||
|
||||
/**
|
||||
* Silently re-fetches the brand list while keeping the current state
|
||||
* visible (no loading flash). Subscribed views re-render from the updated
|
||||
* state — this is the reload pattern for SSE-driven refreshes.
|
||||
*
|
||||
* @returns {Promise<Array<object>>} Fresh brands.
|
||||
*/
|
||||
async reload() {
|
||||
if (inflightLoad) return inflightLoad;
|
||||
inflightLoad = (async () => {
|
||||
try {
|
||||
const brands = await fetchBrands();
|
||||
brandAutomat.setState({ brands, status: 'succeeded', error: null });
|
||||
return brands;
|
||||
} catch (err) {
|
||||
brandAutomat.setState({ error: err?.message ?? String(err) });
|
||||
throw err;
|
||||
} finally {
|
||||
inflightLoad = null;
|
||||
}
|
||||
})();
|
||||
return inflightLoad;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -134,4 +178,21 @@ authAutomat.subscribe((auth) => {
|
||||
}
|
||||
});
|
||||
|
||||
// SSE: single `brand` event with `{ sender, brandId }` payload for every
|
||||
// create/rename/delete/picture change. Silently reloads the list; subscribed
|
||||
// views re-render from state. Own events are skipped.
|
||||
sseAutomat.actions.on('brand', (event) => {
|
||||
try {
|
||||
const payload = JSON.parse(event?.data ?? '{}');
|
||||
if (payload && typeof payload.sender === 'string' && payload.sender) {
|
||||
try {
|
||||
if (payload.sender === getSseClientId()) return;
|
||||
} catch {}
|
||||
}
|
||||
} catch {
|
||||
// Unparseable payload: fall through to reload.
|
||||
}
|
||||
void brandAutomat.actions.reload().catch(() => {});
|
||||
});
|
||||
|
||||
export default brandAutomat;
|
||||
|
||||
Reference in New Issue
Block a user