From 3cffaaab43602d60f12e407fa2011f5ce4dd07e3 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 16 Jun 2021 09:59:56 -0400 Subject: [PATCH] Switch to better tracking of enabled state, use actions, fix deleting item from details page Signed-off-by: Sebastian Malton --- .../catalog-entities/kubernetes-cluster.ts | 1 - src/common/catalog/catalog-entity.ts | 1 - src/main/cluster-manager.ts | 7 +- .../+catalog/catalog-entity-details.tsx | 70 ++++++++----------- .../+catalog/catalog-entity-drawer-menu.tsx | 15 ++-- .../+catalog/catalog-entity.store.ts | 17 +++-- src/renderer/components/+catalog/catalog.tsx | 63 +++++++++-------- .../components/hotbar/hotbar-entity-icon.tsx | 3 +- .../components/hotbar/hotbar-icon.tsx | 21 ++++-- 9 files changed, 104 insertions(+), 94 deletions(-) diff --git a/src/common/catalog-entities/kubernetes-cluster.ts b/src/common/catalog-entities/kubernetes-cluster.ts index d61ba3efb1..c86ad91034 100644 --- a/src/common/catalog-entities/kubernetes-cluster.ts +++ b/src/common/catalog-entities/kubernetes-cluster.ts @@ -114,7 +114,6 @@ export class KubernetesCluster extends CatalogEntity { HotbarStore.getInstance().removeAllHotbarItems(this.getId()); - context.hideDetails(); requestMain(clusterDeleteHandler, this.metadata.uid); }, confirm: { diff --git a/src/common/catalog/catalog-entity.ts b/src/common/catalog/catalog-entity.ts index 611bacf679..8b8d04f6cb 100644 --- a/src/common/catalog/catalog-entity.ts +++ b/src/common/catalog/catalog-entity.ts @@ -135,7 +135,6 @@ export interface CatalogEntitySettingsMenu { export interface CatalogEntityContextMenuContext { navigate: (url: string) => void; - hideDetails: () => void; menuItems: CatalogEntityContextMenu[]; } diff --git a/src/main/cluster-manager.ts b/src/main/cluster-manager.ts index d8ab2d9381..d42b2a6002 100644 --- a/src/main/cluster-manager.ts +++ b/src/main/cluster-manager.ts @@ -62,7 +62,7 @@ export class ClusterManager extends Singleton { observe(this.deleting, change => { if (change.type === "add") { - catalogEntityRegistry.getById(change.newValue).status.phase = "deleting"; + this.updateEntityStatus(catalogEntityRegistry.getById(change.newValue)); } }); @@ -122,12 +122,13 @@ export class ClusterManager extends Singleton { catalogEntityRegistry.items.splice(index, 1, entity); } - protected updateEntityStatus(entity: KubernetesCluster, cluster: Cluster) { + @action + protected updateEntityStatus(entity: KubernetesCluster, cluster?: Cluster) { if (this.deleting.has(entity.getId())) { entity.status.phase = "deleting"; entity.status.enabled = false; } else { - entity.status.phase = cluster.accessible ? "connected" : "disconnected"; + entity.status.phase = cluster?.accessible ? "connected" : "disconnected"; entity.status.enabled = true; } } diff --git a/src/renderer/components/+catalog/catalog-entity-details.tsx b/src/renderer/components/+catalog/catalog-entity-details.tsx index 3dd2fd1ea3..c36a227695 100644 --- a/src/renderer/components/+catalog/catalog-entity-details.tsx +++ b/src/renderer/components/+catalog/catalog-entity-details.tsx @@ -26,28 +26,18 @@ import { Drawer, DrawerItem, DrawerItemLabels } from "../drawer"; import { CatalogEntity, catalogEntityRunContext } from "../../api/catalog-entity"; import type { CatalogCategory } from "../../../common/catalog"; import { Icon } from "../icon"; -import { KubeObject } from "../../api/kube-object"; import { CatalogEntityDrawerMenu } from "./catalog-entity-drawer-menu"; import { CatalogEntityDetailRegistry } from "../../../extensions/registries"; import { HotbarIcon } from "../hotbar/hotbar-icon"; +import type { CatalogEntityItem } from "./catalog-entity.store"; -interface Props { - entity: CatalogEntity; +interface Props { + item: CatalogEntityItem | null | undefined; hideDetails(): void; } @observer -export class CatalogEntityDetails extends Component { - private abortController?: AbortController; - - constructor(props: Props) { - super(props); - } - - componentWillUnmount() { - this.abortController?.abort(); - } - +export class CatalogEntityDetails extends Component> { categoryIcon(category: CatalogCategory) { if (category.metadata.icon.includes("; @@ -56,16 +46,10 @@ export class CatalogEntityDetails extends Component { } } - openEntity() { - this.props.entity.onRun(catalogEntityRunContext); - } - - renderContent() { - const { entity } = this.props; - const labels = KubeObject.stringifyLabels(entity.metadata.labels); - const detailItems = CatalogEntityDetailRegistry.getInstance().getItemsForKind(entity.kind, entity.apiVersion); - const details = detailItems.map((item, index) => { - return ; + renderContent(item: CatalogEntityItem) { + const detailItems = CatalogEntityDetailRegistry.getInstance().getItemsForKind(item.kind, item.apiVersion); + const details = detailItems.map(({ components }, index) => { + return ; }); const showDetails = detailItems.find((item) => item.priority > 999) === undefined; @@ -76,29 +60,35 @@ export class CatalogEntityDetails extends Component {
this.openEntity()} + uid={item.id} + title={item.name} + source={item.source} + icon={item.entity.spec.iconData} + disabled={!item?.enabled} + onClick={() => item.onRun(catalogEntityRunContext)} size={128} /> -
- Click to open -
+ {item?.enabled && ( +
+ Click to open +
+ )}
- {entity.metadata.name} + {item.name} - {entity.kind} + {item.kind} - {entity.metadata.source} + {item.source} + + + {item.phase}
@@ -111,8 +101,8 @@ export class CatalogEntityDetails extends Component { } render() { - const { entity, hideDetails } = this.props; - const title = `${entity.kind}: ${entity.metadata.name}`; + const { item, hideDetails } = this.props; + const title = `${item.kind}: ${item.name}`; return ( { usePortal={true} open={true} title={title} - toolbar={} + toolbar={} onClose={hideDetails} > - {this.renderContent()} + {item && this.renderContent(item)} ); } diff --git a/src/renderer/components/+catalog/catalog-entity-drawer-menu.tsx b/src/renderer/components/+catalog/catalog-entity-drawer-menu.tsx index 5a54b1293b..545eebb933 100644 --- a/src/renderer/components/+catalog/catalog-entity-drawer-menu.tsx +++ b/src/renderer/components/+catalog/catalog-entity-drawer-menu.tsx @@ -30,10 +30,10 @@ import { MenuItem } from "../menu"; import { ConfirmDialog } from "../confirm-dialog"; import { HotbarStore } from "../../../common/hotbar-store"; import { Icon } from "../icon"; +import type { CatalogEntityItem } from "./catalog-entity.store"; export interface CatalogEntityDrawerMenuProps extends MenuActionsProps { - entity: T | null | undefined; - hideDrawer: () => void, + item: CatalogEntityItem | null | undefined; } @observer @@ -49,9 +49,8 @@ export class CatalogEntityDrawerMenu extends React.Comp this.contextMenu = { menuItems: [], navigate: (url: string) => navigate(url), - hideDetails: this.props.hideDrawer, }; - this.props.entity?.onContextMenuOpen(this.contextMenu); + this.props.item?.onContextMenuOpen(this.contextMenu); } onMenuItemClick(menuItem: CatalogEntityContextMenu) { @@ -109,19 +108,19 @@ export class CatalogEntityDrawerMenu extends React.Comp } render() { - if (!this.contextMenu) { + const { className, item: entity, ...menuProps } = this.props; + + if (!this.contextMenu || !entity.enabled) { return null; } - const { className, entity, ...menuProps } = this.props; - return ( - {this.getMenuItems(entity)} + {this.getMenuItems(entity.entity)} ); } diff --git a/src/renderer/components/+catalog/catalog-entity.store.ts b/src/renderer/components/+catalog/catalog-entity.store.ts index 7cc3046c34..c9e0e9bf11 100644 --- a/src/renderer/components/+catalog/catalog-entity.store.ts +++ b/src/renderer/components/+catalog/catalog-entity.store.ts @@ -25,13 +25,17 @@ import type { CatalogEntity, CatalogEntityActionContext } from "../../api/catalo import { ItemObject, ItemStore } from "../../item.store"; import { CatalogCategory, catalogCategoryRegistry } from "../../../common/catalog"; import { autoBind } from "../../../common/utils"; -export class CatalogEntityItem implements ItemObject { - constructor(public entity: CatalogEntity) {} +export class CatalogEntityItem implements ItemObject { + constructor(public entity: T) {} get kind() { return this.entity.kind; } + get apiVersion() { + return this.entity.apiVersion; + } + get name() { return this.entity.metadata.name; } @@ -52,7 +56,7 @@ export class CatalogEntityItem implements ItemObject { return this.entity.status.phase; } - @computed get enabled() { + get enabled() { return this.entity.status.enabled ?? true; } @@ -91,7 +95,7 @@ export class CatalogEntityItem implements ItemObject { } } -export class CatalogEntityStore extends ItemStore { +export class CatalogEntityStore extends ItemStore> { constructor() { super(); makeObservable(this); @@ -99,6 +103,7 @@ export class CatalogEntityStore extends ItemStore { } @observable activeCategory?: CatalogCategory; + @observable selectedItemId?: string; @computed get entities() { if (!this.activeCategory) { @@ -108,6 +113,10 @@ export class CatalogEntityStore extends ItemStore { return catalogEntityRegistry.getItemsForCategory(this.activeCategory).map(entity => new CatalogEntityItem(entity)); } + @computed get selectedItem() { + return this.entities.find(e => e.getId() === this.selectedItemId); + } + watch() { const disposers: IReactionDisposer[] = [ reaction(() => this.entities, () => this.loadAll()), diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx index 306d6ca255..13a827f398 100644 --- a/src/renderer/components/+catalog/catalog.tsx +++ b/src/renderer/components/+catalog/catalog.tsx @@ -32,7 +32,7 @@ import type { CatalogEntityContextMenu, CatalogEntityContextMenuContext } from " import { Badge } from "../badge"; import { HotbarStore } from "../../../common/hotbar-store"; import { ConfirmDialog } from "../confirm-dialog"; -import { catalogCategoryRegistry } from "../../../common/catalog"; +import { catalogCategoryRegistry, CatalogEntity } from "../../../common/catalog"; import { CatalogAddButton } from "./catalog-add-button"; import type { RouteComponentProps } from "react-router"; import { Notifications } from "../notifications"; @@ -59,7 +59,6 @@ export class Catalog extends React.Component { @observable private catalogEntityStore?: CatalogEntityStore; @observable private contextMenu: CatalogEntityContextMenuContext; @observable activeTab?: string; - @observable selectedItem?: CatalogEntityItem; constructor(props: Props) { super(props); @@ -80,7 +79,6 @@ export class Catalog extends React.Component { this.contextMenu = { menuItems: observable.array([]), navigate: (url: string) => navigate(url), - hideDetails: () => this.selectedItem = null, }; this.catalogEntityStore = new CatalogEntityStore(); disposeOnUnmount(this, [ @@ -104,12 +102,12 @@ export class Catalog extends React.Component { ]); } - addToHotbar(item: CatalogEntityItem): void { + addToHotbar(item: CatalogEntityItem): void { HotbarStore.getInstance().addToHotbar(item.entity); } - onDetails = (item: CatalogEntityItem) => { - this.selectedItem = item; + onDetails = (item: CatalogEntityItem) => { + this.catalogEntityStore.selectedItemId = item.getId(); }; onMenuItemClick(menuItem: CatalogEntityContextMenu) { @@ -145,7 +143,7 @@ export class Catalog extends React.Component { return ; } - renderItemMenu = (item: CatalogEntityItem) => { + renderItemMenu = (item: CatalogEntityItem) => { const onOpen = () => { this.contextMenu.menuItems = []; @@ -168,7 +166,7 @@ export class Catalog extends React.Component { ); }; - renderIcon(item: CatalogEntityItem) { + renderIcon(item: CatalogEntityItem) { return ( { store={this.catalogEntityStore} tableId="catalog-items" sortingCallbacks={{ - [sortBy.name]: (item: CatalogEntityItem) => item.name, - [sortBy.source]: (item: CatalogEntityItem) => item.source, - [sortBy.status]: (item: CatalogEntityItem) => item.phase, + [sortBy.name]: (item: CatalogEntityItem) => item.name, + [sortBy.source]: (item: CatalogEntityItem) => item.source, + [sortBy.status]: (item: CatalogEntityItem) => item.phase, }} searchFilters={[ - (entity: CatalogEntityItem) => entity.searchFields, + (entity: CatalogEntityItem) => entity.searchFields, ]} renderTableHeader={[ { title: "", className: css.iconCell }, @@ -203,10 +201,10 @@ export class Catalog extends React.Component { { title: "Labels", className: css.labelsCell }, { title: "Status", className: css.statusCell, sortBy: sortBy.status }, ]} - customizeTableRowProps={(item: CatalogEntityItem) => ({ + customizeTableRowProps={(item: CatalogEntityItem) => ({ disabled: !item.enabled, })} - renderTableContents={(item: CatalogEntityItem) => [ + renderTableContents={(item: CatalogEntityItem) => [ this.renderIcon(item), item.name, item.source, @@ -228,13 +226,13 @@ export class Catalog extends React.Component { store={this.catalogEntityStore} tableId="catalog-items" sortingCallbacks={{ - [sortBy.name]: (item: CatalogEntityItem) => item.name, - [sortBy.kind]: (item: CatalogEntityItem) => item.kind, - [sortBy.source]: (item: CatalogEntityItem) => item.source, - [sortBy.status]: (item: CatalogEntityItem) => item.phase, + [sortBy.name]: (item: CatalogEntityItem) => item.name, + [sortBy.kind]: (item: CatalogEntityItem) => item.kind, + [sortBy.source]: (item: CatalogEntityItem) => item.source, + [sortBy.status]: (item: CatalogEntityItem) => item.phase, }} searchFilters={[ - (entity: CatalogEntityItem) => entity.searchFields, + (entity: CatalogEntityItem) => entity.searchFields, ]} renderTableHeader={[ { title: "", className: css.iconCell }, @@ -244,10 +242,10 @@ export class Catalog extends React.Component { { title: "Labels", className: css.labelsCell }, { title: "Status", className: css.statusCell, sortBy: sortBy.status }, ]} - customizeTableRowProps={(item: CatalogEntityItem) => ({ + customizeTableRowProps={(item: CatalogEntityItem) => ({ disabled: !item.enabled, })} - renderTableContents={(item: CatalogEntityItem) => [ + renderTableContents={(item: CatalogEntityItem) => [ this.renderIcon(item), item.name, item.kind, @@ -255,7 +253,7 @@ export class Catalog extends React.Component { item.labels.map((label) => ), { title: item.phase, className: cssNames(css[item.phase]) } ]} - detailsItem={this.selectedItem} + detailsItem={this.catalogEntityStore.selectedItem} onDetails={this.onDetails} renderItemMenu={this.renderItemMenu} /> @@ -272,15 +270,18 @@ export class Catalog extends React.Component {
{ this.catalogEntityStore.activeCategory ? this.renderSingleCategoryList() : this.renderAllCategoriesList() }
- { !this.selectedItem && ( - - )} - { this.selectedItem && ( - this.selectedItem = null} - /> - )} + { + this.catalogEntityStore.selectedItem + ? ( + this.catalogEntityStore.selectedItemId = null} + /> + ) + : ( + + ) + } ); } diff --git a/src/renderer/components/hotbar/hotbar-entity-icon.tsx b/src/renderer/components/hotbar/hotbar-entity-icon.tsx index 006da8e190..bde9347d39 100644 --- a/src/renderer/components/hotbar/hotbar-entity-icon.tsx +++ b/src/renderer/components/hotbar/hotbar-entity-icon.tsx @@ -27,7 +27,7 @@ import type { CatalogEntity, CatalogEntityContextMenu, CatalogEntityContextMenuC import { catalogCategoryRegistry } from "../../api/catalog-category-registry"; import { catalogEntityRegistry } from "../../api/catalog-entity-registry"; import { navigate } from "../../navigation"; -import { cssNames, IClassName, noop } from "../../utils"; +import { cssNames, IClassName } from "../../utils"; import { Icon } from "../icon"; import { HotbarIcon } from "./hotbar-icon"; import { HotbarStore } from "../../../common/hotbar-store"; @@ -55,7 +55,6 @@ export class HotbarEntityIcon extends React.Component { this.contextMenu = { menuItems: [], navigate: (url: string) => navigate(url), - hideDetails: noop, }; } diff --git a/src/renderer/components/hotbar/hotbar-icon.tsx b/src/renderer/components/hotbar/hotbar-icon.tsx index 0ee8f2ddc8..7d3008231e 100644 --- a/src/renderer/components/hotbar/hotbar-icon.tsx +++ b/src/renderer/components/hotbar/hotbar-icon.tsx @@ -62,7 +62,7 @@ function onMenuItemClick(menuItem: CatalogEntityContextMenu) { } export const HotbarIcon = observer(({menuItems = [], size = 40, ...props}: HotbarIconProps) => { - const { uid, title, icon, active, className, source, disabled, onMenuOpen, children, ...rest } = props; + const { uid, title, icon, active, className, source, disabled, onMenuOpen, onClick, children, ...rest } = props; const id = `hotbarIcon-${uid}`; const [menuOpen, setMenuOpen] = useState(false); @@ -77,7 +77,13 @@ export const HotbarIcon = observer(({menuItems = [], size = 40, ...props}: Hotba src={icon} className={active ? "active" : "default"} width={size} - height={size} />; + height={size} + onClick={(event) => { + if (!disabled) { + onClick?.(event); + } + }} + />; } else { return { + if (!disabled) { + onClick?.(event); + } + }} />; } }; @@ -106,8 +117,10 @@ export const HotbarIcon = observer(({menuItems = [], size = 40, ...props}: Hotba toggleEvent="contextmenu" position={{right: true, bottom: true }} // FIXME: position does not work open={() => { - onMenuOpen?.(); - toggleMenu(); + if (!disabled) { + onMenuOpen?.(); + toggleMenu(); + } }} close={() => toggleMenu()}> { menuItems.map((menuItem) => {