UNPKG

stream-chat

Version:

JS SDK for the Stream Chat API

79 lines (58 loc) 2.55 kB
export type Patch<T> = (value: T) => T; export type ValueOrPatch<T> = T | Patch<T>; export type Handler<T> = (nextValue: T, previousValue: T | undefined) => void; export type Unsubscribe = () => void; export const isPatch = <T>(value: ValueOrPatch<T>): value is Patch<T> => { return typeof value === 'function'; }; export class StateStore<T extends Record<string, unknown>> { private handlerSet = new Set<Handler<T>>(); private static logCount = 5; constructor(private value: T) {} public next = (newValueOrPatch: ValueOrPatch<T>): void => { // newValue (or patch output) should never be mutated previous value const newValue = isPatch(newValueOrPatch) ? newValueOrPatch(this.value) : newValueOrPatch; // do not notify subscribers if the value hasn't changed if (newValue === this.value) return; const oldValue = this.value; this.value = newValue; this.handlerSet.forEach((handler) => handler(this.value, oldValue)); }; public partialNext = (partial: Partial<T>): void => this.next((current) => ({ ...current, ...partial })); public getLatestValue = (): T => this.value; public subscribe = (handler: Handler<T>): Unsubscribe => { handler(this.value, undefined); this.handlerSet.add(handler); return () => { this.handlerSet.delete(handler); }; }; public subscribeWithSelector = <O extends Readonly<Record<string, unknown>> | Readonly<unknown[]>>( selector: (nextValue: T) => O, handler: Handler<O>, ) => { // begin with undefined to reduce amount of selector calls let selectedValues: O | undefined; const wrappedHandler: Handler<T> = (nextValue) => { const newlySelectedValues = selector(nextValue); let hasUpdatedValues = !selectedValues; if (Array.isArray(newlySelectedValues) && StateStore.logCount > 0) { console.warn( '[StreamChat]: The API of our StateStore has changed. Instead of returning an array in the selector, please return a named object of properties.', ); StateStore.logCount--; } for (const key in selectedValues) { // @ts-ignore TODO: remove array support (Readonly<unknown[]>) if (selectedValues[key] === newlySelectedValues[key]) continue; hasUpdatedValues = true; break; } if (!hasUpdatedValues) return; const oldSelectedValues = selectedValues; selectedValues = newlySelectedValues; handler(newlySelectedValues, oldSelectedValues); }; return this.subscribe(wrappedHandler); }; }