UNPKG

@snipsonian/observable-state

Version:

Observable-state snippets (redux-like)

108 lines (93 loc) 4.8 kB
import isArray from '@snipsonian/core/src/is/isArray'; import extendNotificationsToTrigger, { ITriggerParentNotifications } from './extendNotificationsToTrigger'; export interface IStateObserverManager<StateChangeNotificationKey> { // eslint-disable-next-line max-len registerObserver: (props: IRegisterStateObserverProps<StateChangeNotificationKey>) => IStateObserver<StateChangeNotificationKey>; unRegisterObserver: (observer: IStateObserver<StateChangeNotificationKey>) => void; notifyObserversOfStateChanges: (props: INotifyObserversOfStateChangesProps<StateChangeNotificationKey>) => void; } export interface IRegisterStateObserverProps<StateChangeNotificationKey> { observerName: string; notificationsToObserve: StateChangeNotificationKey[]; onNotify: (props: IStateObserverNotifyProps<StateChangeNotificationKey>) => void; } export interface IStateObserver<StateChangeNotificationKey> { id: number; observerName: string; notify: (props: IStateObserverNotifyProps<StateChangeNotificationKey>) => void; } export interface IStateObserverNotifyProps<StateChangeNotificationKey> { notifications: StateChangeNotificationKey[]; } interface INotifyObserversOfStateChangesProps<StateChangeNotificationKey> { notificationsToTrigger: StateChangeNotificationKey[]; triggerParentNotifications: ITriggerParentNotifications; } interface IRegisteredStateObserver<StateChangeNotificationKey> extends IStateObserver<StateChangeNotificationKey> { notificationsToObserve: StateChangeNotificationKey[]; } // eslint-disable-next-line max-len export default function createStateObserverManager<StateChangeNotificationKey>(): IStateObserverManager<StateChangeNotificationKey> { let observerCounter = 0; const observerMap: { [observerId: number]: IRegisteredStateObserver<StateChangeNotificationKey> } = {}; return { registerObserver: ({ observerName, notificationsToObserve, onNotify, }: IRegisterStateObserverProps<StateChangeNotificationKey>) => { observerCounter += 1; const observer: IStateObserver<StateChangeNotificationKey> = { id: observerCounter, observerName, notify: onNotify, }; observerMap[observer.id] = { ...observer, notificationsToObserve, }; return observer; }, unRegisterObserver: (observer: IStateObserver<StateChangeNotificationKey>) => { delete observerMap[observer.id]; }, notifyObserversOfStateChanges: ({ notificationsToTrigger, triggerParentNotifications, }: INotifyObserversOfStateChangesProps<StateChangeNotificationKey>) => { if (isArray(notificationsToTrigger) && notificationsToTrigger.length > 0) { const uniqueObserversToNotify = extendNotificationsToTrigger({ notificationsToTrigger, triggerParentNotifications, }) .reduce( (accumulator, notificationToTrigger) => { const matchingObservers = Object.values(observerMap) .filter((registeredObserver) => registeredObserver.notificationsToObserve.includes(notificationToTrigger)); matchingObservers.forEach((observer) => { const alreadyFoundObserver = accumulator .find((observerToNotify) => observerToNotify.id === observer.id); if (alreadyFoundObserver) { alreadyFoundObserver.notifications.push(notificationToTrigger); } else { accumulator.push({ ...observer, notifications: [notificationToTrigger], }); } }); return accumulator; }, // eslint-disable-next-line max-len [] as (IStateObserver<StateChangeNotificationKey> & IStateObserverNotifyProps<StateChangeNotificationKey>)[], ); if (uniqueObserversToNotify && uniqueObserversToNotify.length > 0) { uniqueObserversToNotify.forEach((observerToNotify) => { observerToNotify.notify({ notifications: observerToNotify.notifications }); }); } } }, }; }