import { normalize, denormalize, Schema } from 'normalizr'; import { Keys, Entities, USER_LITERAL } from './entities'; import { cloneDeep, merge, pickBy } from 'lodash'; import { Id } from '../types'; import { EventTopic } from './event-topic'; export const CHANGE_EVENT_TYPE = 'changed'; export type Data = { [Keys.userLiteral]: { [key: string]: number | undefined; }; [Keys.addresses]: { [key: string]: {}; }; [Keys.banks]: { [key: string]: {}; }; [Keys.credits]: { [key: string]: {}; }; [Keys.forms]: { [key: string]: {}; }; [Keys.kycs]: { [key: string]: {}; }; [Keys.loanParts]: { [key: string]: {}; }; [Keys.persons]: { [key: string]: {}; }; [Keys.tinkLoans]: { [key: string]: {}; }; [Keys.users]: { [key: string]: {}; }; [Keys.countries]: { [key: string]: {}; }; [Keys.currencies]: { [key: string]: {}; }; [Keys.markets]: { [key: string]: {}; }; [Keys.genders]: { [key: string]: {}; }; [Keys.employmentTypes]: { [key: string]: {}; }; [Keys.kycPurposes]: { [key: string]: {}; }; [Keys.kycSources]: { [key: string]: {}; }; }; export type Options = { expanded?: boolean; deep?: boolean; }; const initData = { [Keys.userLiteral]: { [USER_LITERAL]: undefined, }, [Keys.addresses]: {}, [Keys.banks]: {}, [Keys.credits]: {}, [Keys.forms]: {}, [Keys.kycs]: {}, [Keys.loanParts]: {}, [Keys.persons]: {}, [Keys.tinkLoans]: {}, [Keys.tinkAccounts]: {}, [Keys.users]: {}, [Keys.countries]: {}, [Keys.currencies]: {}, [Keys.markets]: {}, [Keys.genders]: {}, [Keys.employmentTypes]: {}, [Keys.creditTypes]: {}, [Keys.kycPurposes]: {}, [Keys.kycSources]: {}, }; export class Store { private _data: Data; private _eventBus: EventTopic; constructor( eventBus: EventTopic = new EventTopic(), initialData: Data = initData, ) { this._data = initialData; this._eventBus = eventBus; } reset(data: Data = initData) { this._data = data; this._eventBus.dispatchEvent({ type: CHANGE_EVENT_TYPE }); } has(key: Keys, id: Id) { return !!this._data[key][`${id}`]; } find(key: Keys, fn: (item: any, index: number, array: any[]) => boolean) { const result = Object.values(this._data[key]).find(fn); return cloneDeep(result); } filter(key: Keys, fn: (item: any, index: number, array: any[]) => boolean) { const result = Object.values(this._data[key]).filter(fn); return cloneDeep(result); } get(key: Keys, id?: Id, opts: Options = {}) { if (!id) { return; } if (opts.expanded) { return denormalize(id, Entities[key], this._data); } return cloneDeep(this._data[key][`${id}`]); } getAll(key: Keys, opts: Options = {}) { if (opts.expanded) { return denormalize( Object.keys(this._data[key]), [Entities[key]], this._data, ); } return cloneDeep(Object.values(this._data[key])); } set(key: Keys, item: any, opts: Options = { deep: true }) { const { entities } = normalize(item, Entities[key]); if (opts.deep) { this._data = merge({}, this._data, entities); } else { this._data = { ...this._data, ...entities, }; } this._eventBus.dispatchEvent({ type: CHANGE_EVENT_TYPE }); } setBySchema(schema: Schema, data: any, opts: Options = { deep: true }) { const { entities } = normalize(data, schema); if (opts.deep) { this._data = merge({}, this._data, entities); } else { this._data = { ...this._data, ...entities, }; } this._eventBus.dispatchEvent({ type: CHANGE_EVENT_TYPE }); } del(key: Keys, id: Id | Id[]) { const ids = Array.isArray(id) ? id : [id]; const entity = Entities[key]; const entityList = Object.values(Entities).map((current) => { return { key: current.key, props: pickBy( // @ts-ignore current.schema, (cEnt) => { return Array.isArray(cEnt) ? cEnt[0] === entity : cEnt === entity; }, ), }; }); entityList.forEach((ent) => { Object.values(this._data[ent.key]).forEach((item: any) => { Object.entries(ent.props).forEach(([propName, prop]) => { if (Array.isArray(prop)) { item[propName] = item[propName].filter( (cId: Id) => !ids.includes(cId), ); } else { delete item[propName]; } }); }); }); ids.forEach((id) => { delete this._data[key][id]; }); this._eventBus.dispatchEvent({ type: CHANGE_EVENT_TYPE }); } }