import orderBy from "lodash/orderBy"; import { autobind, noop } from "./utils"; import { action, computed, observable, when } from "mobx"; export interface ItemObject { getId(): string; getName(): string; } @autobind() export abstract class ItemStore { abstract loadAll(): Promise; protected defaultSorting = (item: T): string => item.getName(); @observable isLoading = false; @observable isLoaded = false; @observable items = observable.array([], { deep: false }); @observable selectedItemsIds = observable.map(); @computed get selectedItems(): T[] { return this.items.filter(item => this.selectedItemsIds.get(item.getId())); } getByName(name: string, ...args: any[]): T; getByName(name: string): T { return this.items.find(item => item.getName() === name); } @action protected sortItems(items: any[], sorting?: ((item: any) => E)[], order?: "asc" | "desc"): any[] { if (!sorting) { return orderBy(items, [this.defaultSorting], order); } return orderBy(items, sorting, order); } protected async createItem(params: { name: string; namespace?: string }, data?: Partial): Promise; protected async createItem(...args: D[]): Promise; @action protected async createItem(request: () => Promise): Promise { const newItem = await request(); const item = this.items.find(item => item.getId() === newItem.getId()); if (item) { return item; } else { const items = this.sortItems([...this.items, newItem]); this.items.replace(items); return newItem; } } protected async loadItems(...args: any[]): Promise; @action protected async loadItems(request: () => Promise, sortItems = true): Promise { if (this.isLoading) { await when(() => !this.isLoading); return; } this.isLoading = true; try { let items = await request(); if (sortItems) { items = this.sortItems(items); } this.items.replace(items); this.isLoaded = true; } finally { this.isLoading = false; } } protected async loadItem(params: { name: string; namespace?: string }): Promise; protected async loadItem(...args: D[]): Promise @action protected async loadItem(request: () => Promise, sortItems = true): Promise { const item = await request().catch(() => null); if (item) { const existingItem = this.items.find(el => el.getId() === item.getId()); if (existingItem) { const index = this.items.findIndex(item => item === existingItem); this.items.splice(index, 1, item); } else { let items = [...this.items, item]; if (sortItems) { items = this.sortItems(items); } this.items.replace(items); } return item; } } @action protected async updateItem(item: T, request: () => Promise): Promise { const updatedItem = await request(); const index = this.items.findIndex(i => i.getId() === item.getId()); this.items.splice(index, 1, updatedItem); return updatedItem; } @action protected async removeItem(item: T, request: () => Promise): Promise { await request(); this.items.remove(item); this.selectedItemsIds.delete(item.getId()); } isSelected(item: T): boolean { return !!this.selectedItemsIds.get(item.getId()); } @action select(item: T): void { this.selectedItemsIds.set(item.getId(), true); } @action unselect(item: T): void { this.selectedItemsIds.delete(item.getId()); } @action toggleSelection(item: T): void { if (this.isSelected(item)) { this.unselect(item); } else { this.select(item); } } @action toggleSelectionAll(visibleItems: T[] = this.items): void { const allSelected = visibleItems.every(this.isSelected); if (allSelected) { visibleItems.forEach(this.unselect); } else { visibleItems.forEach(this.select); } } isSelectedAll(visibleItems: T[] = this.items): boolean { if (!visibleItems.length) { return false; } return visibleItems.every(this.isSelected); } @action resetSelection(): void { this.selectedItemsIds.clear(); } @action reset(): void { this.resetSelection(); this.items.clear(); this.selectedItemsIds.clear(); this.isLoaded = false; this.isLoading = false; } async removeSelectedItems?(): Promise; subscribe(..._args: any[]): () => void { return noop; } *[Symbol.iterator](): Generator { yield* this.items; } }