mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
fix: catalog entity context menu stale/unobservable
Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
parent
c05f3b1f61
commit
f68264a933
@ -19,16 +19,15 @@
|
|||||||
* 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, makeObservable } from "mobx";
|
import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from "mobx";
|
||||||
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
|
import { catalogEntityRegistry } from "../../api/catalog-entity-registry";
|
||||||
import type { CatalogEntity, CatalogEntityActionContext } from "../../api/catalog-entity";
|
import type { CatalogEntity, CatalogEntityActionContext } from "../../api/catalog-entity";
|
||||||
import { ItemObject, ItemStore } from "../../item.store";
|
import { ItemObject, ItemStore } from "../../item.store";
|
||||||
import type { CatalogCategory } from "../../../common/catalog";
|
import { CatalogCategory } from "../../../common/catalog";
|
||||||
|
import { autoBind } from "../../../common/utils";
|
||||||
|
|
||||||
export class CatalogEntityItem implements ItemObject {
|
export class CatalogEntityItem implements ItemObject {
|
||||||
constructor(public entity: CatalogEntity) {
|
constructor(public entity: CatalogEntity) {}
|
||||||
makeObservable(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
get name() {
|
get name() {
|
||||||
return this.entity.metadata.name;
|
return this.entity.metadata.name;
|
||||||
@ -86,20 +85,32 @@ export class CatalogEntityItem implements ItemObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class CatalogEntityStore extends ItemStore<CatalogEntityItem> {
|
export class CatalogEntityStore extends ItemStore<CatalogEntityItem> {
|
||||||
getEntities(category?: CatalogCategory): CatalogEntityItem[] {
|
constructor() {
|
||||||
if (!category) {
|
super();
|
||||||
|
makeObservable(this);
|
||||||
|
autoBind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@observable activeCategory?: CatalogCategory;
|
||||||
|
|
||||||
|
@computed get entities() {
|
||||||
|
if (!this.activeCategory) {
|
||||||
return catalogEntityRegistry.items.map(entity => new CatalogEntityItem(entity));
|
return catalogEntityRegistry.items.map(entity => new CatalogEntityItem(entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
return catalogEntityRegistry.getItemsForCategory(category)
|
return catalogEntityRegistry.getItemsForCategory(this.activeCategory).map(entity => new CatalogEntityItem(entity));
|
||||||
.map(entity => new CatalogEntityItem(entity));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadItems(entities: CatalogEntityItem[]) {
|
watch() {
|
||||||
return super.loadItems(() => entities);
|
const disposers: IReactionDisposer[] = [
|
||||||
|
reaction(() => this.entities, () => this.loadAll()),
|
||||||
|
reaction(() => this.activeCategory, () => this.loadAll(), { delay: 100})
|
||||||
|
];
|
||||||
|
|
||||||
|
return () => disposers.forEach((dispose) => dispose());
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadAll() {
|
loadAll() {
|
||||||
return this.loadItems(this.getEntities());
|
return this.loadItems(() => this.entities);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,23 +23,22 @@ 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 { computed, makeObservable, reaction } from "mobx";
|
import { action, makeObservable, observable, reaction, when } 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 { MenuActions, MenuItem } from "../menu";
|
import { MenuItem, MenuActions } from "../menu";
|
||||||
import { CatalogCategory, CatalogEntityContextMenu, CatalogEntityContextMenuContext, catalogEntityRunContext } from "../../api/catalog-entity";
|
import { 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 { ConfirmDialog } from "../confirm-dialog";
|
import { ConfirmDialog } from "../confirm-dialog";
|
||||||
import { Tab, Tabs } from "../tabs";
|
import { Tab, Tabs } from "../tabs";
|
||||||
import { catalogCategoryRegistry } from "../../../common/catalog";
|
import { catalogCategoryRegistry } from "../../../common/catalog";
|
||||||
import { CatalogAddButton } from "./catalog-add-button";
|
import { CatalogAddButton } from "./catalog-add-button";
|
||||||
import type { RouteComponentProps } from "react-router";
|
import type { RouteComponentProps } from "react-router";
|
||||||
import type { ICatalogViewRouteParam } from "./catalog.route";
|
import type { ICatalogViewRouteParam } from "./catalog.route";
|
||||||
import { catalogURL } from "./catalog.route";
|
import { Notifications } from "../notifications";
|
||||||
|
|
||||||
enum sortBy {
|
enum sortBy {
|
||||||
name = "name",
|
name = "name",
|
||||||
@ -47,46 +46,52 @@ enum sortBy {
|
|||||||
status = "status"
|
status = "status"
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props extends RouteComponentProps<ICatalogViewRouteParam> {
|
interface Props extends RouteComponentProps<ICatalogViewRouteParam> {}
|
||||||
}
|
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class Catalog extends React.Component<Props> {
|
export class Catalog extends React.Component<Props> {
|
||||||
|
@observable private catalogEntityStore?: CatalogEntityStore;
|
||||||
|
@observable private contextMenu: CatalogEntityContextMenuContext;
|
||||||
|
@observable activeTab?: string;
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
makeObservable(this);
|
makeObservable(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private catalogEntityStore = new CatalogEntityStore();
|
get routeActiveTab(): string | undefined {
|
||||||
|
const { group, kind } = this.props.match.params ?? {};
|
||||||
|
|
||||||
private contextMenu: CatalogEntityContextMenuContext = {
|
if (group && kind) {
|
||||||
menuItems: [],
|
return `${group}/${kind}`;
|
||||||
navigate: (url: string) => navigate(url),
|
}
|
||||||
};
|
|
||||||
|
|
||||||
get routeParams(): ICatalogViewRouteParam {
|
return undefined;
|
||||||
return this.props.match.params ?? {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get activeCategory(): CatalogCategory | undefined {
|
async componentDidMount() {
|
||||||
const { group, kind } = this.routeParams;
|
this.contextMenu = {
|
||||||
|
menuItems: [],
|
||||||
return catalogCategoryRegistry.getForGroupKind(group, kind);
|
navigate: (url: string) => navigate(url)
|
||||||
}
|
};
|
||||||
|
this.catalogEntityStore = new CatalogEntityStore();
|
||||||
@computed get categories(): CatalogCategory[] {
|
|
||||||
return catalogCategoryRegistry.items;
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed get entities(): CatalogEntityItem[] {
|
|
||||||
return this.catalogEntityStore.getEntities(this.activeCategory);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
disposeOnUnmount(this, [
|
disposeOnUnmount(this, [
|
||||||
// autofill catalog entities into store
|
this.catalogEntityStore.watch(),
|
||||||
reaction(() => this.entities, items => this.catalogEntityStore.loadItems(items), {
|
when(() => catalogCategoryRegistry.items.length > 0, () => {
|
||||||
fireImmediately: true,
|
const item = catalogCategoryRegistry.items.find(i => i.getId() === this.routeActiveTab);
|
||||||
|
|
||||||
|
if (item || this.routeActiveTab === undefined) {
|
||||||
|
this.activeTab = this.routeActiveTab;
|
||||||
|
this.catalogEntityStore.activeCategory = item;
|
||||||
|
} else {
|
||||||
|
Notifications.error(<p>Unknown category: {this.routeActiveTab}</p>);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
reaction(() => catalogCategoryRegistry.items, (items) => {
|
||||||
|
if (!this.activeTab && items.length > 0) {
|
||||||
|
this.activeTab = items[0].getId();
|
||||||
|
this.catalogEntityStore.activeCategory = items[0];
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -116,24 +121,21 @@ export class Catalog extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onTabChange = (categoryId: string) => {
|
get categories() {
|
||||||
const { group, kind } = CatalogCategory.parseId(categoryId);
|
return catalogCategoryRegistry.items;
|
||||||
|
}
|
||||||
|
|
||||||
if (group && kind) {
|
@action
|
||||||
navigate(catalogURL({ params: { group, kind } }));
|
onTabChange = (tabId: string | null) => {
|
||||||
} else {
|
const activeCategory = this.categories.find(category => category.getId() === tabId);
|
||||||
navigate(catalogURL());
|
|
||||||
}
|
this.catalogEntityStore.activeCategory = activeCategory;
|
||||||
|
this.activeTab = activeCategory?.getId();
|
||||||
};
|
};
|
||||||
|
|
||||||
renderNavigation() {
|
renderNavigation() {
|
||||||
return (
|
return (
|
||||||
<Tabs
|
<Tabs className="flex column" scrollable={false} onChange={this.onTabChange} value={this.activeTab}>
|
||||||
className="flex column"
|
|
||||||
scrollable={false}
|
|
||||||
value={this.activeCategory?.getId()}
|
|
||||||
onChange={this.onTabChange}
|
|
||||||
>
|
|
||||||
<div className="sidebarHeader">Catalog</div>
|
<div className="sidebarHeader">Catalog</div>
|
||||||
<div className="sidebarTabs">
|
<div className="sidebarTabs">
|
||||||
<Tab
|
<Tab
|
||||||
@ -157,8 +159,7 @@ export class Catalog extends React.Component<Props> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
renderItemMenu = (item: CatalogEntityItem) => {
|
||||||
renderItemMenu(item: CatalogEntityItem) {
|
|
||||||
const menuItems = this.contextMenu.menuItems.filter((menuItem) => !menuItem.onlyVisibleForSource || menuItem.onlyVisibleForSource === item.entity.metadata.source);
|
const menuItems = this.contextMenu.menuItems.filter((menuItem) => !menuItem.onlyVisibleForSource || menuItem.onlyVisibleForSource === item.entity.metadata.source);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -170,14 +171,18 @@ export class Catalog extends React.Component<Props> {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
<MenuItem key="add-to-hotbar" onClick={() => this.addToHotbar(item)}>
|
<MenuItem key="add-to-hotbar" onClick={() => this.addToHotbar(item) }>
|
||||||
Pin to Hotbar
|
Pin to Hotbar
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</MenuActions>
|
</MenuActions>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
if (!this.catalogEntityStore) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout
|
<PageLayout
|
||||||
className="CatalogPage"
|
className="CatalogPage"
|
||||||
@ -185,7 +190,7 @@ export class Catalog extends React.Component<Props> {
|
|||||||
provideBackButtonNavigation={false}
|
provideBackButtonNavigation={false}
|
||||||
contentGaps={false}>
|
contentGaps={false}>
|
||||||
<ItemListLayout
|
<ItemListLayout
|
||||||
renderHeaderTitle={this.activeCategory?.metadata.name ?? "Browse All"}
|
renderHeaderTitle={this.catalogEntityStore.activeCategory?.metadata.name ?? "Browse All"}
|
||||||
isClusterScoped
|
isClusterScoped
|
||||||
isSearchable={true}
|
isSearchable={true}
|
||||||
isSelectable={false}
|
isSelectable={false}
|
||||||
@ -209,13 +214,13 @@ export class Catalog extends React.Component<Props> {
|
|||||||
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.activeCategory}/>
|
<CatalogAddButton category={this.catalogEntityStore.activeCategory} />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user