ednl-liftstatus-web-components
Version:
The EDNL LiftStatus web components
121 lines (120 loc) • 5.01 kB
JavaScript
import { createStore } from "@stencil/store";
import dayjs from "dayjs";
import { isEmpty } from "lodash-es";
import PubSub from "pubsub-js";
const ID_KEY_PREFIX = "ednl-ls-";
const DATA_TOPIC_POSTFIX = "-data";
const SUBS_TOPIC_POSTFIX = "-subs";
const storesContainer = createStore({
updatedAt: Date.now(),
stores: new Map(),
});
export function createNewStore(idKey, initialState) {
// Gather initial state, use defaults if not provided
const newStore = createStore({
historicalSensorData: (initialState === null || initialState === void 0 ? void 0 : initialState.historicalSensorData) || [],
currentSensorData: (initialState === null || initialState === void 0 ? void 0 : initialState.currentSensorData) || {
lastUpdate: 0,
values: {},
updated: {},
},
datePicker: (initialState === null || initialState === void 0 ? void 0 : initialState.datePicker) || {
start: dayjs()
.hour(dayjs().hour())
.minute(0)
.second(0)
.millisecond(0)
.toISOString(),
end: dayjs()
.hour(dayjs().hour() + 1)
.minute(0)
.second(0)
.millisecond(0)
.toISOString(), // End date/time in ISO 8601 string
},
error: (initialState === null || initialState === void 0 ? void 0 : initialState.error) || {
message: "",
type: "",
},
hasBackDoor: (initialState === null || initialState === void 0 ? void 0 : initialState.hasBackDoor) || false,
});
// Create new store
storesContainer.state.stores.set(ID_KEY_PREFIX + idKey, newStore);
// Update updatedAt to notify subscribers
storesContainer.set("updatedAt", Date.now());
// Listen for new subscriptions and react by publishing the current state
PubSub.subscribe(ID_KEY_PREFIX + idKey + SUBS_TOPIC_POSTFIX, () => {
publishData(idKey);
});
// Publish the data for existing subscribers
publishData(idKey);
}
export async function getStore(idKey) {
const idKeyWithPrefix = ID_KEY_PREFIX + idKey;
// We need to return a promise for stencil lifecycle methods to be able to await
// See: https://stenciljs.com/docs/component-lifecycle#async-lifecycle-methods
return new Promise((resolve, reject) => {
// If no idKey is provided, return first store, but only if there is only one store in the container
// We do this to allow backwards compatibility with the old way of using the web components (where there was only one installation)
// Multiple stores and no idKey indicates user error
if (isEmpty(idKey) && storesContainer.state.stores.size > 1) {
reject("Add the idKey to the <ls-data> component like so: <ls-data id-key='yourUniqueKey'></ls-data>");
}
// If no idKey is provided, return first store
if (isEmpty(idKey) &&
!isEmpty(storesContainer.state.stores.values().next().value)) {
resolve(storesContainer.state.stores.values().next().value);
}
// If store exists, return it
if (!isEmpty(idKey) &&
!isEmpty(storesContainer.state.stores.get(idKeyWithPrefix))) {
resolve(storesContainer.state.stores.get(idKeyWithPrefix));
}
// If store doesn't exist, wait for it to be created
storesContainer.onChange("updatedAt", () => {
// Multiple stores and no idKey indicates user error
if (isEmpty(idKey) && storesContainer.state.stores.size > 1) {
reject("Add the idKey to the <ls-data> component like so: <ls-data id-key='yourUniqueKey'></ls-data>");
}
// If no idKey is provided, return first store
if (isEmpty(idKey) &&
!isEmpty(storesContainer.state.stores.values().next().value)) {
resolve(storesContainer.state.stores.values().next().value);
}
// If store exists, return it
if (!isEmpty(idKey) &&
!isEmpty(storesContainer.state.stores.get(idKeyWithPrefix))) {
resolve(storesContainer.state.stores.get(idKeyWithPrefix));
}
});
});
}
export async function updateStore(currentSensorData, idKey) {
const store = await getStore(idKey);
// Update sensor data
store.set("currentSensorData", currentSensorData);
// Update updatedAt to notify subscribers
storesContainer.set("updatedAt", Date.now());
// Publish data for external use
publishData(idKey);
}
/**
* This method publishes current state op the store to the data topic.
* This is used for external applications to subscribe to. Customers can install PubSubJS and subscribe to the data topic.
* Other components use the store directly.
*/
async function publishData(idKey) {
const dataTopic = ID_KEY_PREFIX + idKey + DATA_TOPIC_POSTFIX;
const subscriptionCount = PubSub.countSubscriptions(dataTopic);
const store = await getStore(idKey);
const state = {
currentSensorData: store.state.currentSensorData,
error: store.state.error,
hasBackDoor: store.state.hasBackDoor,
};
// Publish the data for existing subscribers
if (subscriptionCount > 0) {
PubSub.publish(dataTopic, state);
}
}
//# sourceMappingURL=store.js.map