diff --git a/src/common/__tests__/hotbar-store.test.ts b/src/common/__tests__/hotbar-store.test.ts index 92e5a3f35b..7979e9bb5e 100644 --- a/src/common/__tests__/hotbar-store.test.ts +++ b/src/common/__tests__/hotbar-store.test.ts @@ -21,6 +21,7 @@ import mockFs from "mock-fs"; import { CatalogEntityItem } from "../../renderer/components/+catalog/catalog-entity.store"; +import { CatalogEntity } from "../catalog"; import { ClusterStore } from "../cluster-store"; import { HotbarStore } from "../hotbar-store"; @@ -159,10 +160,9 @@ describe("HotbarStore", () => { it("adds items", () => { const hotbarStore = HotbarStore.createInstance(); - const entity = new CatalogEntityItem(testCluster); hotbarStore.load(); - hotbarStore.addToHotbar(entity); + hotbarStore.addToHotbar(testCluster); const items = hotbarStore.getActive().items.filter(Boolean); expect(items.length).toEqual(1); @@ -170,10 +170,9 @@ describe("HotbarStore", () => { it("removes items", () => { const hotbarStore = HotbarStore.createInstance(); - const entity = new CatalogEntityItem(testCluster); hotbarStore.load(); - hotbarStore.addToHotbar(entity); + hotbarStore.addToHotbar(testCluster); hotbarStore.removeFromHotbar("test"); const items = hotbarStore.getActive().items.filter(Boolean); @@ -182,10 +181,9 @@ describe("HotbarStore", () => { it("does nothing if removing with invalid uid", () => { const hotbarStore = HotbarStore.createInstance(); - const entity = new CatalogEntityItem(testCluster); hotbarStore.load(); - hotbarStore.addToHotbar(entity); + hotbarStore.addToHotbar(testCluster); hotbarStore.removeFromHotbar("invalid uid"); const items = hotbarStore.getActive().items.filter(Boolean); @@ -194,14 +192,11 @@ describe("HotbarStore", () => { it("moves item to empty cell", () => { const hotbarStore = HotbarStore.createInstance(); - const test = new CatalogEntityItem(testCluster); - const minikube = new CatalogEntityItem(minikubeCluster); - const aws = new CatalogEntityItem(awsCluster); hotbarStore.load(); - hotbarStore.addToHotbar(test); - hotbarStore.addToHotbar(minikube); - hotbarStore.addToHotbar(aws); + hotbarStore.addToHotbar(testCluster); + hotbarStore.addToHotbar(minikubeCluster); + hotbarStore.addToHotbar(awsCluster); expect(hotbarStore.getActive().items[5]).toBeNull(); @@ -213,14 +208,11 @@ describe("HotbarStore", () => { it("moves items down", () => { const hotbarStore = HotbarStore.createInstance(); - const test = new CatalogEntityItem(testCluster); - const minikube = new CatalogEntityItem(minikubeCluster); - const aws = new CatalogEntityItem(awsCluster); hotbarStore.load(); - hotbarStore.addToHotbar(test); - hotbarStore.addToHotbar(minikube); - hotbarStore.addToHotbar(aws); + hotbarStore.addToHotbar(testCluster); + hotbarStore.addToHotbar(minikubeCluster); + hotbarStore.addToHotbar(awsCluster); // aws -> test hotbarStore.restackItems(2, 0); @@ -232,14 +224,11 @@ describe("HotbarStore", () => { it("moves items up", () => { const hotbarStore = HotbarStore.createInstance(); - const test = new CatalogEntityItem(testCluster); - const minikube = new CatalogEntityItem(minikubeCluster); - const aws = new CatalogEntityItem(awsCluster); hotbarStore.load(); - hotbarStore.addToHotbar(test); - hotbarStore.addToHotbar(minikube); - hotbarStore.addToHotbar(aws); + hotbarStore.addToHotbar(testCluster); + hotbarStore.addToHotbar(minikubeCluster); + hotbarStore.addToHotbar(awsCluster); // test -> aws hotbarStore.restackItems(0, 2); @@ -251,10 +240,9 @@ describe("HotbarStore", () => { it("does nothing when item moved to same cell", () => { const hotbarStore = HotbarStore.createInstance(); - const test = new CatalogEntityItem(testCluster); hotbarStore.load(); - hotbarStore.addToHotbar(test); + hotbarStore.addToHotbar(testCluster); hotbarStore.restackItems(0, 0); expect(hotbarStore.getActive().items[0].entity.uid).toEqual("test"); @@ -262,15 +250,12 @@ describe("HotbarStore", () => { it("new items takes first empty cell", () => { const hotbarStore = HotbarStore.createInstance(); - const test = new CatalogEntityItem(testCluster); - const minikube = new CatalogEntityItem(minikubeCluster); - const aws = new CatalogEntityItem(awsCluster); hotbarStore.load(); - hotbarStore.addToHotbar(test); - hotbarStore.addToHotbar(aws); + hotbarStore.addToHotbar(testCluster); + hotbarStore.addToHotbar(awsCluster); hotbarStore.restackItems(0, 3); - hotbarStore.addToHotbar(minikube); + hotbarStore.addToHotbar(minikubeCluster); expect(hotbarStore.getActive().items[0].entity.uid).toEqual("minikube"); }); @@ -282,10 +267,9 @@ describe("HotbarStore", () => { console.error = jest.fn(); const hotbarStore = HotbarStore.createInstance(); - const test = new CatalogEntityItem(testCluster); hotbarStore.load(); - hotbarStore.addToHotbar(test); + hotbarStore.addToHotbar(testCluster); expect(() => hotbarStore.restackItems(-5, 0)).toThrow(); expect(() => hotbarStore.restackItems(2, -1)).toThrow(); diff --git a/src/common/hotbar-store.ts b/src/common/hotbar-store.ts index b2d5af0f2a..53c1c5b3ef 100644 --- a/src/common/hotbar-store.ts +++ b/src/common/hotbar-store.ts @@ -23,8 +23,8 @@ import { action, comparer, observable, toJS } from "mobx"; import { BaseStore } from "./base-store"; import migrations from "../migrations/hotbar-store"; import * as uuid from "uuid"; -import { CatalogEntityItem } from "../renderer/components/+catalog/catalog-entity.store"; import isNull from "lodash/isNull"; +import { CatalogEntity } from "./catalog"; export interface HotbarItem { entity: { @@ -151,15 +151,15 @@ export class HotbarStore extends BaseStore { } @action - addToHotbar(item: CatalogEntityItem, cellIndex = -1) { + addToHotbar(item: CatalogEntity, cellIndex = -1) { const hotbar = this.getActive(); const newItem = { entity: { - uid: item.id, - name: item.name, - source: item.source + uid: item.metadata.uid, + name: item.metadata.name, + source: item.metadata.source }}; - if (hotbar.items.find(i => i?.entity.uid === item.id)) { + if (hotbar.items.find(i => i?.entity.uid === item.metadata.uid)) { return; } diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx index 2fb6ce8f9f..3f0505d3c1 100644 --- a/src/renderer/components/+catalog/catalog.tsx +++ b/src/renderer/components/+catalog/catalog.tsx @@ -68,7 +68,7 @@ export class Catalog extends React.Component { } addToHotbar(item: CatalogEntityItem): void { - HotbarStore.getInstance().addToHotbar(item); + HotbarStore.getInstance().addToHotbar(item.entity); } onDetails(item: CatalogEntityItem) { @@ -137,7 +137,7 @@ export class Catalog extends React.Component { return ( item.onContextMenuOpen(this.contextMenu)}> this.addToHotbar(item) }> - Add to Hotbar + Pin to Hotbar { menuItems.map((menuItem, index) => ( diff --git a/src/renderer/components/hotbar/hotbar-entity-icon.tsx b/src/renderer/components/hotbar/hotbar-entity-icon.tsx index 7e2b7f79bf..72e04e2c5c 100644 --- a/src/renderer/components/hotbar/hotbar-entity-icon.tsx +++ b/src/renderer/components/hotbar/hotbar-entity-icon.tsx @@ -23,21 +23,22 @@ import "./hotbar-icon.scss"; import React, { DOMAttributes } from "react"; import { observable } from "mobx"; import { observer } from "mobx-react"; -import randomColor from "randomcolor"; -import { CatalogEntity, CatalogEntityContextMenu, CatalogEntityContextMenuContext } from "../../../common/catalog"; +import { CatalogEntity, CatalogEntityContextMenuContext } from "../../../common/catalog"; import { catalogCategoryRegistry } from "../../api/catalog-category-registry"; import { catalogEntityRegistry } from "../../api/catalog-entity-registry"; import { navigate } from "../../navigation"; import { cssNames, IClassName } from "../../utils"; -import { ConfirmDialog } from "../confirm-dialog"; import { Icon } from "../icon"; import { HotbarIcon } from "./hotbar-icon"; +import { HotbarStore } from "../../../common/hotbar-store"; interface Props extends DOMAttributes { entity: CatalogEntity; + index: number; className?: IClassName; errorClass?: IClassName; + add: (item: CatalogEntity, index: number) => void; remove: (uid: string) => void; } @@ -77,33 +78,18 @@ export class HotbarEntityIcon extends React.Component { return catalogEntityRegistry.activeEntity?.metadata?.uid == item.getId(); } - onMenuItemClick(menuItem: CatalogEntityContextMenu) { - if (menuItem.confirm) { - ConfirmDialog.open({ - okButtonProps: { - primary: false, - accent: true, - }, - ok: () => { - menuItem.onClick(); - }, - message: menuItem.confirm.message - }); - } else { - menuItem.onClick(); - } - } - - generateAvatarStyle(entity: CatalogEntity): React.CSSProperties { - return { - "backgroundColor": randomColor({ seed: `${entity.metadata.name}-${entity.metadata.source}`, luminosity: "dark" }) - }; + isPersisted(entity: CatalogEntity) { + return HotbarStore.getInstance().getActive().items.find((item) => item?.entity?.uid === entity.metadata.uid) !== undefined; } render() { + if (!this.contextMenu) { + return null; + } + const { - entity, errorClass, remove, - children, ...elemProps + entity, errorClass, add, remove, + index, children, ...elemProps } = this.props; const className = cssNames("HotbarEntityIcon", this.props.className, { interactive: true, @@ -113,16 +99,33 @@ export class HotbarEntityIcon extends React.Component { const onOpen = async () => { await entity.onContextMenuOpen(this.contextMenu); }; + const isActive = this.isActive(entity); + const isPersisted = this.isPersisted(entity); const menuItems = this.contextMenu?.menuItems.filter((menuItem) => !menuItem.onlyVisibleForSource || menuItem.onlyVisibleForSource === entity.metadata.source); + console.log(entity.metadata.name, isActive); + + if (!isPersisted) { + menuItems.unshift({ + title: "Pin to Hotbar", + icon: "push_pin", + onClick: () => add(entity, index) + }); + } else { + menuItems.unshift({ + title: "Unpin from Hotbar", + icon: "push_pin", + onClick: () => remove(entity.metadata.uid) + }); + } + return ( { uid: string; title: string; source: string; - remove: (uid: string) => void; onMenuOpen?: () => void; className?: IClassName; active?: boolean; @@ -85,7 +84,7 @@ function getNameParts(name: string): string[] { } export function HotbarIcon(props: Props) { - const { uid, title, className, source, active, remove, disabled, menuItems, onMenuOpen, children, ...rest } = props; + const { uid, title, className, source, active, disabled, menuItems, onMenuOpen, children, ...rest } = props; const id = `hotbarIcon-${uid}`; const [menuOpen, setMenuOpen] = useState(false); @@ -114,7 +113,7 @@ export function HotbarIcon(props: Props) { {getIconString()} @@ -134,12 +133,6 @@ export function HotbarIcon(props: Props) { toggleMenu(); }} close={() => toggleMenu()}> - { - evt.stopPropagation(); - remove(uid); - }}> - Remove from Hotbar - { menuItems.map((menuItem) => { return ( onMenuItemClick(menuItem) }> diff --git a/src/renderer/components/hotbar/hotbar-menu.tsx b/src/renderer/components/hotbar/hotbar-menu.tsx index 319b05f946..33a8d4e0b7 100644 --- a/src/renderer/components/hotbar/hotbar-menu.tsx +++ b/src/renderer/components/hotbar/hotbar-menu.tsx @@ -28,11 +28,12 @@ import { HotbarEntityIcon } from "./hotbar-entity-icon"; import { cssNames, IClassName } from "../../utils"; import { catalogEntityRegistry } from "../../api/catalog-entity-registry"; import { defaultHotbarCells, HotbarItem, HotbarStore } from "../../../common/hotbar-store"; -import { catalogEntityRunContext } from "../../api/catalog-entity"; +import { CatalogEntity, catalogEntityRunContext } from "../../api/catalog-entity"; import { DragDropContext, Draggable, Droppable, DropResult } from "react-beautiful-dnd"; import { HotbarSelector } from "./hotbar-selector"; import { HotbarCell } from "./hotbar-cell"; import { HotbarIcon } from "./hotbar-icon"; +import { computed } from "mobx"; interface Props { className?: IClassName; @@ -64,6 +65,10 @@ export class HotbarMenu extends React.Component { const from = parseInt(source.droppableId); const to = parseInt(destination.droppableId); + if (!this.hotbar.items[from]) { // Dropped non-persisted item + this.hotbar.items[from] = this.items[from]; + } + HotbarStore.getInstance().restackItems(from, to); } @@ -73,14 +78,38 @@ export class HotbarMenu extends React.Component { hotbar.removeFromHotbar(uid); } + addItem(entity: CatalogEntity, index = -1) { + const hotbar = HotbarStore.getInstance(); + + hotbar.addToHotbar(entity, index); + } + getMoveAwayDirection(entityId: string, cellIndex: number) { const draggableItemIndex = this.hotbar.items.findIndex(item => item?.entity.uid == entityId); return draggableItemIndex > cellIndex ? "animateDown" : "animateUp"; } + @computed get items() { + const items = this.hotbar.items; + const activeEntity = catalogEntityRegistry.activeEntity; + + if (!activeEntity) return items; + + const emptyIndex = items.indexOf(null); + + if (emptyIndex === -1) return items; + if (items.find((item) => item?.entity?.uid === activeEntity.metadata.uid)) return items; + + const modifiedItems = [...items]; + + modifiedItems.splice(emptyIndex, 1, { entity: { uid: activeEntity.metadata.uid }}); + + return modifiedItems; + } + renderGrid() { - return this.hotbar.items.map((item, index) => { + return this.items.map((item, index) => { const entity = this.getEntity(item); return ( @@ -116,17 +145,18 @@ export class HotbarMenu extends React.Component { {entity ? ( entity.onRun(catalogEntityRunContext)} className={cssNames({ isDragging: snapshot.isDragging })} remove={this.removeItem} + add={this.addItem} /> ) : ( )} @@ -151,7 +181,7 @@ export class HotbarMenu extends React.Component { return (
- + {this.renderGrid()}