1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

catalog.tsx / catalog-entities.store.ts refactoring & fixes

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman 2021-05-13 16:37:08 +03:00
parent 68a6ed52c0
commit 4cfe3b1521
5 changed files with 66 additions and 53 deletions

View File

@ -45,6 +45,10 @@ export class CatalogCategoryRegistry {
return Array.from(this.categories); return Array.from(this.categories);
} }
getById(id: string): CatalogCategory{
return this.categories.find(category => category.getId() === id);
}
getForGroupKind<T extends CatalogCategory>(group: string, kind: string) { getForGroupKind<T extends CatalogCategory>(group: string, kind: string) {
return this.categories.find((c) => c.spec.group === group && c.spec.names.kind === kind) as T; return this.categories.find((c) => c.spec.group === group && c.spec.names.kind === kind) as T;
} }

View File

@ -25,7 +25,7 @@ import { CatalogCategory, CatalogEntity, CatalogEntityData, catalogCategoryRegis
import "../../common/catalog-entities"; import "../../common/catalog-entities";
export class CatalogEntityRegistry { export class CatalogEntityRegistry {
@observable protected _items: CatalogEntity[] = observable.array([], { deep: true }); @observable protected _items: CatalogEntity[] = observable.array();
@observable protected _activeEntity: CatalogEntity; @observable protected _activeEntity: CatalogEntity;
constructor(private categoryRegistry: CatalogCategoryRegistry) { constructor(private categoryRegistry: CatalogCategoryRegistry) {

View File

@ -19,11 +19,11 @@
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/ */
import { action, computed, IReactionDisposer, observable, reaction, makeObservable } from "mobx"; import { action, autorun, computed, makeObservable, observable } from "mobx";
import { catalogEntityRegistry } from "../../api/catalog-entity-registry"; import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
import { CatalogEntity, CatalogEntityActionContext } from "../../api/catalog-entity"; import { CatalogEntity, CatalogEntityActionContext } from "../../api/catalog-entity";
import { ItemObject, ItemStore } from "../../item.store"; import { ItemObject, ItemStore } from "../../item.store";
import { autoBind } from "../../utils"; import { autoBind, disposer } from "../../utils";
import { CatalogCategory } from "../../../common/catalog"; import { CatalogCategory } from "../../../common/catalog";
export class CatalogEntityItem implements ItemObject { export class CatalogEntityItem implements ItemObject {
@ -87,13 +87,15 @@ export class CatalogEntityItem implements ItemObject {
} }
export class CatalogEntityStore extends ItemStore<CatalogEntityItem> { export class CatalogEntityStore extends ItemStore<CatalogEntityItem> {
@observable activeCategory?: CatalogCategory; dispose = disposer();
@observable.ref activeCategory?: CatalogCategory;
constructor() { constructor() {
super(); super();
makeObservable(this); makeObservable(this);
autoBind(this); autoBind(this);
this.bindAutoLoading();
} }
@computed get entities() { @computed get entities() {
@ -104,16 +106,24 @@ export class CatalogEntityStore extends ItemStore<CatalogEntityItem> {
return catalogEntityRegistry.getItemsForCategory(this.activeCategory).map(entity => new CatalogEntityItem(entity)); return catalogEntityRegistry.getItemsForCategory(this.activeCategory).map(entity => new CatalogEntityItem(entity));
} }
watch() { protected bindAutoLoading() {
const disposers: IReactionDisposer[] = [ const disposer = autorun(() => {
reaction(() => this.entities, () => this.loadAll()), this.loadItem(this.activeCategory); // preload active category
reaction(() => this.activeCategory, () => this.loadAll(), { delay: 100}) this.loadItems(this.entities); // preload all available entities
]; });
return () => disposers.forEach((dispose) => dispose()); this.dispose.push(disposer);
} }
loadAll() { async loadItem(category: CatalogCategory): Promise<CatalogEntityItem> {
return this.loadItems(() => this.entities); return super.loadItem(() => category);
}
async loadItems(entities: CatalogEntityItem[] = []) {
return super.loadItems(() => entities);
}
async loadAll() {
return this.loadItems();
} }
} }

View File

@ -23,14 +23,14 @@ import "./catalog.scss";
import React from "react"; import React from "react";
import { disposeOnUnmount, observer } from "mobx-react"; import { disposeOnUnmount, observer } from "mobx-react";
import { ItemListLayout } from "../item-object-list"; import { ItemListLayout } from "../item-object-list";
import { action, observable, reaction, makeObservable } from "mobx"; import { autorun, computed, makeObservable, observable } from "mobx";
import { CatalogEntityItem, CatalogEntityStore } from "./catalog-entity.store"; import { CatalogEntityItem, CatalogEntityStore } from "./catalog-entity.store";
import { navigate } from "../../navigation"; import { navigate } from "../../navigation";
import { kebabCase } from "lodash"; import { kebabCase } from "lodash";
import { PageLayout } from "../layout/page-layout"; import { PageLayout } from "../layout/page-layout";
import { MenuItem, MenuActions } from "../menu"; import { MenuActions, MenuItem } from "../menu";
import { Icon } from "../icon"; import { Icon } from "../icon";
import { CatalogEntityContextMenu, CatalogEntityContextMenuContext, catalogEntityRunContext } from "../../api/catalog-entity"; import { CatalogCategory, CatalogEntityContextMenu, CatalogEntityContextMenuContext, catalogEntityRunContext } from "../../api/catalog-entity";
import { Badge } from "../badge"; import { Badge } from "../badge";
import { HotbarStore } from "../../../common/hotbar-store"; import { HotbarStore } from "../../../common/hotbar-store";
import { boundMethod } from "../../utils"; import { boundMethod } from "../../utils";
@ -44,31 +44,37 @@ enum sortBy {
source = "source", source = "source",
status = "status" status = "status"
} }
@observer @observer
export class Catalog extends React.Component { export class Catalog extends React.Component {
@observable private catalogEntityStore?: CatalogEntityStore; private catalogEntityStore = new CatalogEntityStore();
@observable.deep private contextMenu: CatalogEntityContextMenuContext;
@observable activeTab?: string; private contextMenu: CatalogEntityContextMenuContext = {
menuItems: [],
navigate: (url: string) => navigate(url),
};
@observable activeCategoryId = this.categories[0]?.getId();
@computed get activeCategory(): CatalogCategory {
return catalogCategoryRegistry.getById(this.activeCategoryId);
}
@computed get categories(): CatalogCategory[] {
return catalogCategoryRegistry.items;
}
constructor(props: {}) { constructor(props: {}) {
super(props); super(props);
makeObservable(this); makeObservable(this);
} }
async componentDidMount() { componentDidMount() {
this.contextMenu = {
menuItems: [],
navigate: (url: string) => navigate(url)
};
this.catalogEntityStore = new CatalogEntityStore();
disposeOnUnmount(this, [ disposeOnUnmount(this, [
this.catalogEntityStore.watch(), () => this.catalogEntityStore.dispose(), // stop all events, loaders, etc.
reaction(() => catalogCategoryRegistry.items, (items) => { autorun(() => {
if (!this.activeTab && items.length > 0) { this.catalogEntityStore.activeCategory = this.activeCategory; // sync
this.activeTab = items[0].getId(); }),
this.catalogEntityStore.activeCategory = items[0];
}
}, { fireImmediately: true })
]); ]);
} }
@ -97,21 +103,14 @@ export class Catalog extends React.Component {
} }
} }
get categories() {
return catalogCategoryRegistry.items;
}
@action
onTabChange = (tabId: string | null) => {
const activeCategory = this.categories.find(category => category.getId() === tabId);
this.catalogEntityStore.activeCategory = activeCategory;
this.activeTab = activeCategory?.getId();
};
renderNavigation() { renderNavigation() {
return ( return (
<Tabs className="flex column" scrollable={false} onChange={this.onTabChange} value={this.activeTab}> <Tabs
className="flex column"
scrollable={false}
value={this.activeCategoryId}
onChange={(categoryId: string) => this.activeCategoryId = categoryId}
>
<div className="sidebarHeader">Catalog</div> <div className="sidebarHeader">Catalog</div>
<div className="sidebarTabs"> <div className="sidebarTabs">
<Tab <Tab
@ -141,13 +140,13 @@ export class Catalog extends React.Component {
return ( return (
<MenuActions onOpen={() => item.onContextMenuOpen(this.contextMenu)}> <MenuActions onOpen={() => item.onContextMenuOpen(this.contextMenu)}>
<MenuItem key="add-to-hotbar" onClick={() => this.addToHotbar(item) }> <MenuItem key="add-to-hotbar" onClick={() => this.addToHotbar(item)}>
<Icon material="add" small interactive={true} title="Add to hotbar"/> Add to Hotbar <Icon material="add" small interactive={true} title="Add to hotbar"/> Add to Hotbar
</MenuItem> </MenuItem>
{ {
menuItems.map((menuItem, index) => ( menuItems.map((menuItem, index) => (
<MenuItem key={index} onClick={() => this.onMenuItemClick(menuItem)}> <MenuItem key={index} onClick={() => this.onMenuItemClick(menuItem)}>
<Icon material={menuItem.icon} small interactive={true} title={menuItem.title} /> {menuItem.title} <Icon material={menuItem.icon} small interactive={true} title={menuItem.title}/> {menuItem.title}
</MenuItem> </MenuItem>
)) ))
} }
@ -156,8 +155,8 @@ export class Catalog extends React.Component {
} }
render() { render() {
if (!this.catalogEntityStore) { if (!this.activeCategory) {
return null; return "";
} }
return ( return (
@ -167,7 +166,7 @@ export class Catalog extends React.Component {
provideBackButtonNavigation={false} provideBackButtonNavigation={false}
contentGaps={false}> contentGaps={false}>
<ItemListLayout <ItemListLayout
renderHeaderTitle={this.catalogEntityStore.activeCategory?.metadata.name ?? "Browse All"} renderHeaderTitle={this.activeCategory.metadata.name ?? "Browse All"}
isClusterScoped isClusterScoped
isSearchable={true} isSearchable={true}
isSelectable={false} isSelectable={false}
@ -191,13 +190,13 @@ export class Catalog extends React.Component {
renderTableContents={(item: CatalogEntityItem) => [ renderTableContents={(item: CatalogEntityItem) => [
item.name, item.name,
item.source, item.source,
item.labels.map((label) => <Badge key={label} label={label} title={label} />), item.labels.map((label) => <Badge key={label} label={label} title={label}/>),
{ title: item.phase, className: kebabCase(item.phase) } { title: item.phase, className: kebabCase(item.phase) }
]} ]}
onDetails={(item: CatalogEntityItem) => this.onDetails(item) } onDetails={(item: CatalogEntityItem) => this.onDetails(item)}
renderItemMenu={this.renderItemMenu} renderItemMenu={this.renderItemMenu}
/> />
<CatalogAddButton category={this.catalogEntityStore.activeCategory} /> <CatalogAddButton category={this.activeCategory}/>
</PageLayout> </PageLayout>
); );
} }

View File

@ -120,7 +120,7 @@ export abstract class ItemStore<T extends ItemObject = ItemObject> {
protected async loadItem(...args: any[]): Promise<T> protected async loadItem(...args: any[]): Promise<T>
@action @action
protected async loadItem(request: () => Promise<T>, sortItems = true) { protected async loadItem(request: () => Promise<T>, sortItems = true) {
const item = await request().catch(() => null); const item = await Promise.resolve(request()).catch(() => null);
if (item) { if (item) {
const existingItem = this.items.find(el => el.getId() === item.getId()); const existingItem = this.items.find(el => el.getId() === item.getId());