mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Make legacy unit tests for hotbar green and more simple by using the new legacy helper
Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>
This commit is contained in:
parent
b935dbca78
commit
eb756f4da4
@ -6,14 +6,17 @@
|
|||||||
import { anyObject } from "jest-mock-extended";
|
import { anyObject } from "jest-mock-extended";
|
||||||
import mockFs from "mock-fs";
|
import mockFs from "mock-fs";
|
||||||
import logger from "../../main/logger";
|
import logger from "../../main/logger";
|
||||||
import type { CatalogEntity, CatalogEntityData, CatalogEntityKindData } from "../catalog";
|
import type {
|
||||||
|
CatalogEntity,
|
||||||
|
CatalogEntityData,
|
||||||
|
CatalogEntityKindData,
|
||||||
|
} from "../catalog";
|
||||||
import { HotbarStore } from "../hotbar-store";
|
import { HotbarStore } from "../hotbar-store";
|
||||||
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../main/getDiForUnitTesting";
|
||||||
import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable";
|
|
||||||
import writeFileInjectable from "../fs/write-file.injectable";
|
|
||||||
import hotbarStoreInjectable from "../hotbar-store.injectable";
|
import hotbarStoreInjectable from "../hotbar-store.injectable";
|
||||||
import catalogCatalogEntityInjectable
|
import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable";
|
||||||
from "../catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable";
|
import appVersionInjectable from "../get-configuration-file-model/app-version/app-version.injectable";
|
||||||
|
import type { DiContainer } from "@ogre-tools/injectable";
|
||||||
|
|
||||||
jest.mock("../../main/catalog/catalog-entity-registry", () => ({
|
jest.mock("../../main/catalog/catalog-entity-registry", () => ({
|
||||||
catalogEntityRegistry: {
|
catalogEntityRegistry: {
|
||||||
@ -21,9 +24,11 @@ jest.mock("../../main/catalog/catalog-entity-registry", () => ({
|
|||||||
getMockCatalogEntity({
|
getMockCatalogEntity({
|
||||||
apiVersion: "v1",
|
apiVersion: "v1",
|
||||||
kind: "Cluster",
|
kind: "Cluster",
|
||||||
|
|
||||||
status: {
|
status: {
|
||||||
phase: "Running",
|
phase: "Running",
|
||||||
},
|
},
|
||||||
|
|
||||||
metadata: {
|
metadata: {
|
||||||
uid: "1dfa26e2ebab15780a3547e9c7fa785c",
|
uid: "1dfa26e2ebab15780a3547e9c7fa785c",
|
||||||
name: "mycluster",
|
name: "mycluster",
|
||||||
@ -31,12 +36,15 @@ jest.mock("../../main/catalog/catalog-entity-registry", () => ({
|
|||||||
labels: {},
|
labels: {},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getMockCatalogEntity({
|
getMockCatalogEntity({
|
||||||
apiVersion: "v1",
|
apiVersion: "v1",
|
||||||
kind: "Cluster",
|
kind: "Cluster",
|
||||||
|
|
||||||
status: {
|
status: {
|
||||||
phase: "Running",
|
phase: "Running",
|
||||||
},
|
},
|
||||||
|
|
||||||
metadata: {
|
metadata: {
|
||||||
uid: "55b42c3c7ba3b04193416cda405269a5",
|
uid: "55b42c3c7ba3b04193416cda405269a5",
|
||||||
name: "my_shiny_cluster",
|
name: "my_shiny_cluster",
|
||||||
@ -44,12 +52,15 @@ jest.mock("../../main/catalog/catalog-entity-registry", () => ({
|
|||||||
labels: {},
|
labels: {},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getMockCatalogEntity({
|
getMockCatalogEntity({
|
||||||
apiVersion: "v1",
|
apiVersion: "v1",
|
||||||
kind: "Cluster",
|
kind: "Cluster",
|
||||||
|
|
||||||
status: {
|
status: {
|
||||||
phase: "Running",
|
phase: "Running",
|
||||||
},
|
},
|
||||||
|
|
||||||
metadata: {
|
metadata: {
|
||||||
uid: "catalog-entity",
|
uid: "catalog-entity",
|
||||||
name: "Catalog",
|
name: "Catalog",
|
||||||
@ -61,7 +72,9 @@ jest.mock("../../main/catalog/catalog-entity-registry", () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function getMockCatalogEntity(data: Partial<CatalogEntityData> & CatalogEntityKindData): CatalogEntity {
|
function getMockCatalogEntity(
|
||||||
|
data: Partial<CatalogEntityData> & CatalogEntityKindData,
|
||||||
|
): CatalogEntity {
|
||||||
return {
|
return {
|
||||||
getName: jest.fn(() => data.metadata?.name),
|
getName: jest.fn(() => data.metadata?.name),
|
||||||
getId: jest.fn(() => data.metadata?.uid),
|
getId: jest.fn(() => data.metadata?.uid),
|
||||||
@ -116,275 +129,303 @@ const awsCluster = getMockCatalogEntity({
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("HotbarStore", () => {
|
describe("HotbarStore", () => {
|
||||||
|
let di: DiContainer;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||||
|
|
||||||
di.override(writeFileInjectable, () => () => undefined);
|
// @ts-ignore
|
||||||
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
|
di.permitSideEffects(hotbarStoreInjectable);
|
||||||
|
|
||||||
di.override(hotbarStoreInjectable, (di) =>
|
// @ts-ignore
|
||||||
HotbarStore.createInstance({
|
di.permitSideEffects(getConfigurationFileModelInjectable);
|
||||||
catalogCatalogEntity: di.inject(catalogCatalogEntityInjectable),
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
mockFs({
|
// @ts-ignore
|
||||||
"some-directory-for-user-data": {
|
di.permitSideEffects(appVersionInjectable);
|
||||||
"lens-hotbar-store.json": JSON.stringify({}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await di.runSetups();
|
|
||||||
|
|
||||||
di.inject(hotbarStoreInjectable);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
mockFs.restore();
|
mockFs.restore();
|
||||||
HotbarStore.resetInstance();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("load", () => {
|
describe("given no migrations", () => {
|
||||||
it("loads one hotbar by default", () => {
|
beforeEach(async () => {
|
||||||
expect(HotbarStore.getInstance().hotbars.length).toEqual(1);
|
await di.runSetups();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("load", () => {
|
||||||
|
it("loads one hotbar by default", () => {
|
||||||
|
expect(HotbarStore.getInstance().hotbars.length).toEqual(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("add", () => {
|
||||||
|
it("adds a hotbar", () => {
|
||||||
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
|
hotbarStore.add({ name: "hottest" });
|
||||||
|
expect(hotbarStore.hotbars.length).toEqual(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("hotbar items", () => {
|
||||||
|
it("initially creates 12 empty cells", () => {
|
||||||
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
|
expect(hotbarStore.getActive().items.length).toEqual(12);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("initially adds catalog entity as first item", () => {
|
||||||
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
|
expect(hotbarStore.getActive().items[0].entity.name).toEqual("Catalog");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("adds items", () => {
|
||||||
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
|
hotbarStore.addToHotbar(testCluster);
|
||||||
|
const items = hotbarStore.getActive().items.filter(Boolean);
|
||||||
|
|
||||||
|
expect(items.length).toEqual(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("removes items", () => {
|
||||||
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
|
hotbarStore.addToHotbar(testCluster);
|
||||||
|
hotbarStore.removeFromHotbar("test");
|
||||||
|
hotbarStore.removeFromHotbar("catalog-entity");
|
||||||
|
const items = hotbarStore.getActive().items.filter(Boolean);
|
||||||
|
|
||||||
|
expect(items).toStrictEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does nothing if removing with invalid uid", () => {
|
||||||
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
|
hotbarStore.addToHotbar(testCluster);
|
||||||
|
hotbarStore.removeFromHotbar("invalid uid");
|
||||||
|
const items = hotbarStore.getActive().items.filter(Boolean);
|
||||||
|
|
||||||
|
expect(items.length).toEqual(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("moves item to empty cell", () => {
|
||||||
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
|
hotbarStore.addToHotbar(testCluster);
|
||||||
|
hotbarStore.addToHotbar(minikubeCluster);
|
||||||
|
hotbarStore.addToHotbar(awsCluster);
|
||||||
|
|
||||||
|
expect(hotbarStore.getActive().items[6]).toBeNull();
|
||||||
|
|
||||||
|
hotbarStore.restackItems(1, 5);
|
||||||
|
|
||||||
|
expect(hotbarStore.getActive().items[5]).toBeTruthy();
|
||||||
|
expect(hotbarStore.getActive().items[5].entity.uid).toEqual("test");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("moves items down", () => {
|
||||||
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
|
hotbarStore.addToHotbar(testCluster);
|
||||||
|
hotbarStore.addToHotbar(minikubeCluster);
|
||||||
|
hotbarStore.addToHotbar(awsCluster);
|
||||||
|
|
||||||
|
// aws -> catalog
|
||||||
|
hotbarStore.restackItems(3, 0);
|
||||||
|
|
||||||
|
const items = hotbarStore
|
||||||
|
.getActive()
|
||||||
|
.items.map((item) => item?.entity.uid || null);
|
||||||
|
|
||||||
|
expect(items.slice(0, 4)).toEqual([
|
||||||
|
"aws",
|
||||||
|
"catalog-entity",
|
||||||
|
"test",
|
||||||
|
"minikube",
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("moves items up", () => {
|
||||||
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
|
hotbarStore.addToHotbar(testCluster);
|
||||||
|
hotbarStore.addToHotbar(minikubeCluster);
|
||||||
|
hotbarStore.addToHotbar(awsCluster);
|
||||||
|
|
||||||
|
// test -> aws
|
||||||
|
hotbarStore.restackItems(1, 3);
|
||||||
|
|
||||||
|
const items = hotbarStore
|
||||||
|
.getActive()
|
||||||
|
.items.map((item) => item?.entity.uid || null);
|
||||||
|
|
||||||
|
expect(items.slice(0, 4)).toEqual([
|
||||||
|
"catalog-entity",
|
||||||
|
"minikube",
|
||||||
|
"aws",
|
||||||
|
"test",
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs an error if cellIndex is out of bounds", () => {
|
||||||
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
|
hotbarStore.add({ name: "hottest", id: "hottest" });
|
||||||
|
hotbarStore.setActiveHotbar("hottest");
|
||||||
|
|
||||||
|
const { error } = logger;
|
||||||
|
const mocked = jest.fn();
|
||||||
|
|
||||||
|
logger.error = mocked;
|
||||||
|
|
||||||
|
hotbarStore.addToHotbar(testCluster, -1);
|
||||||
|
expect(mocked).toBeCalledWith(
|
||||||
|
"[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range",
|
||||||
|
anyObject(),
|
||||||
|
);
|
||||||
|
|
||||||
|
hotbarStore.addToHotbar(testCluster, 12);
|
||||||
|
expect(mocked).toBeCalledWith(
|
||||||
|
"[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range",
|
||||||
|
anyObject(),
|
||||||
|
);
|
||||||
|
|
||||||
|
hotbarStore.addToHotbar(testCluster, 13);
|
||||||
|
expect(mocked).toBeCalledWith(
|
||||||
|
"[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range",
|
||||||
|
anyObject(),
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.error = error;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws an error if getId is invalid or returns not a string", () => {
|
||||||
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
|
expect(() => hotbarStore.addToHotbar({} as any)).toThrowError(
|
||||||
|
TypeError,
|
||||||
|
);
|
||||||
|
expect(() =>
|
||||||
|
hotbarStore.addToHotbar({ getId: () => true } as any),
|
||||||
|
).toThrowError(TypeError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws an error if getName is invalid or returns not a string", () => {
|
||||||
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
hotbarStore.addToHotbar({ getId: () => "" } as any),
|
||||||
|
).toThrowError(TypeError);
|
||||||
|
expect(() =>
|
||||||
|
hotbarStore.addToHotbar({ getId: () => "", getName: () => 4 } as any),
|
||||||
|
).toThrowError(TypeError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does nothing when item moved to same cell", () => {
|
||||||
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
|
hotbarStore.addToHotbar(testCluster);
|
||||||
|
hotbarStore.restackItems(1, 1);
|
||||||
|
|
||||||
|
expect(hotbarStore.getActive().items[1].entity.uid).toEqual("test");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("new items takes first empty cell", () => {
|
||||||
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
|
hotbarStore.addToHotbar(testCluster);
|
||||||
|
hotbarStore.addToHotbar(awsCluster);
|
||||||
|
hotbarStore.restackItems(0, 3);
|
||||||
|
hotbarStore.addToHotbar(minikubeCluster);
|
||||||
|
|
||||||
|
expect(hotbarStore.getActive().items[0].entity.uid).toEqual("minikube");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws if invalid arguments provided", () => {
|
||||||
|
// Prevent writing to stderr during this render.
|
||||||
|
const { error, warn } = console;
|
||||||
|
|
||||||
|
console.error = jest.fn();
|
||||||
|
console.warn = jest.fn();
|
||||||
|
|
||||||
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
|
hotbarStore.addToHotbar(testCluster);
|
||||||
|
|
||||||
|
expect(() => hotbarStore.restackItems(-5, 0)).toThrow();
|
||||||
|
expect(() => hotbarStore.restackItems(2, -1)).toThrow();
|
||||||
|
expect(() => hotbarStore.restackItems(14, 1)).toThrow();
|
||||||
|
expect(() => hotbarStore.restackItems(11, 112)).toThrow();
|
||||||
|
|
||||||
|
// Restore writing to stderr.
|
||||||
|
console.error = error;
|
||||||
|
console.warn = warn;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("checks if entity already pinned to hotbar", () => {
|
||||||
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
|
||||||
|
hotbarStore.addToHotbar(testCluster);
|
||||||
|
|
||||||
|
expect(hotbarStore.isAddedToActive(testCluster)).toBeTruthy();
|
||||||
|
expect(hotbarStore.isAddedToActive(awsCluster)).toBeFalsy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("add", () => {
|
describe("given pre beta-5 configurations", () => {
|
||||||
it("adds a hotbar", () => {
|
beforeEach(async () => {
|
||||||
const hotbarStore = HotbarStore.getInstance();
|
const configurationToBeMigrated = {
|
||||||
|
"some-electron-app-path-for-user-data": {
|
||||||
hotbarStore.add({ name: "hottest" });
|
|
||||||
expect(hotbarStore.hotbars.length).toEqual(2);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("hotbar items", () => {
|
|
||||||
it("initially creates 12 empty cells", () => {
|
|
||||||
const hotbarStore = HotbarStore.getInstance();
|
|
||||||
|
|
||||||
expect(hotbarStore.getActive().items.length).toEqual(12);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("initially adds catalog entity as first item", () => {
|
|
||||||
const hotbarStore = HotbarStore.getInstance();
|
|
||||||
|
|
||||||
expect(hotbarStore.getActive().items[0].entity.name).toEqual("Catalog");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("adds items", () => {
|
|
||||||
const hotbarStore = HotbarStore.getInstance();
|
|
||||||
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
const items = hotbarStore.getActive().items.filter(Boolean);
|
|
||||||
|
|
||||||
expect(items.length).toEqual(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("removes items", () => {
|
|
||||||
const hotbarStore = HotbarStore.getInstance();
|
|
||||||
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
hotbarStore.removeFromHotbar("test");
|
|
||||||
hotbarStore.removeFromHotbar("catalog-entity");
|
|
||||||
const items = hotbarStore.getActive().items.filter(Boolean);
|
|
||||||
|
|
||||||
expect(items).toStrictEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does nothing if removing with invalid uid", () => {
|
|
||||||
const hotbarStore = HotbarStore.getInstance();
|
|
||||||
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
hotbarStore.removeFromHotbar("invalid uid");
|
|
||||||
const items = hotbarStore.getActive().items.filter(Boolean);
|
|
||||||
|
|
||||||
expect(items.length).toEqual(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("moves item to empty cell", () => {
|
|
||||||
const hotbarStore = HotbarStore.getInstance();
|
|
||||||
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
hotbarStore.addToHotbar(minikubeCluster);
|
|
||||||
hotbarStore.addToHotbar(awsCluster);
|
|
||||||
|
|
||||||
expect(hotbarStore.getActive().items[6]).toBeNull();
|
|
||||||
|
|
||||||
hotbarStore.restackItems(1, 5);
|
|
||||||
|
|
||||||
expect(hotbarStore.getActive().items[5]).toBeTruthy();
|
|
||||||
expect(hotbarStore.getActive().items[5].entity.uid).toEqual("test");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("moves items down", () => {
|
|
||||||
const hotbarStore = HotbarStore.getInstance();
|
|
||||||
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
hotbarStore.addToHotbar(minikubeCluster);
|
|
||||||
hotbarStore.addToHotbar(awsCluster);
|
|
||||||
|
|
||||||
// aws -> catalog
|
|
||||||
hotbarStore.restackItems(3, 0);
|
|
||||||
|
|
||||||
const items = hotbarStore.getActive().items.map(item => item?.entity.uid || null);
|
|
||||||
|
|
||||||
expect(items.slice(0, 4)).toEqual(["aws", "catalog-entity", "test", "minikube"]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("moves items up", () => {
|
|
||||||
const hotbarStore = HotbarStore.getInstance();
|
|
||||||
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
hotbarStore.addToHotbar(minikubeCluster);
|
|
||||||
hotbarStore.addToHotbar(awsCluster);
|
|
||||||
|
|
||||||
// test -> aws
|
|
||||||
hotbarStore.restackItems(1, 3);
|
|
||||||
|
|
||||||
const items = hotbarStore.getActive().items.map(item => item?.entity.uid || null);
|
|
||||||
|
|
||||||
expect(items.slice(0, 4)).toEqual(["catalog-entity", "minikube", "aws", "test"]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("logs an error if cellIndex is out of bounds", () => {
|
|
||||||
const hotbarStore = HotbarStore.getInstance();
|
|
||||||
|
|
||||||
hotbarStore.add({ name: "hottest", id: "hottest" });
|
|
||||||
hotbarStore.setActiveHotbar("hottest");
|
|
||||||
|
|
||||||
const { error } = logger;
|
|
||||||
const mocked = jest.fn();
|
|
||||||
|
|
||||||
logger.error = mocked;
|
|
||||||
|
|
||||||
hotbarStore.addToHotbar(testCluster, -1);
|
|
||||||
expect(mocked).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject());
|
|
||||||
|
|
||||||
hotbarStore.addToHotbar(testCluster, 12);
|
|
||||||
expect(mocked).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject());
|
|
||||||
|
|
||||||
hotbarStore.addToHotbar(testCluster, 13);
|
|
||||||
expect(mocked).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject());
|
|
||||||
|
|
||||||
logger.error = error;
|
|
||||||
});
|
|
||||||
|
|
||||||
it("throws an error if getId is invalid or returns not a string", () => {
|
|
||||||
const hotbarStore = HotbarStore.getInstance();
|
|
||||||
|
|
||||||
expect(() => hotbarStore.addToHotbar({} as any)).toThrowError(TypeError);
|
|
||||||
expect(() => hotbarStore.addToHotbar({ getId: () => true } as any)).toThrowError(TypeError);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("throws an error if getName is invalid or returns not a string", () => {
|
|
||||||
const hotbarStore = HotbarStore.getInstance();
|
|
||||||
|
|
||||||
expect(() => hotbarStore.addToHotbar({ getId: () => "" } as any)).toThrowError(TypeError);
|
|
||||||
expect(() => hotbarStore.addToHotbar({ getId: () => "", getName: () => 4 } as any)).toThrowError(TypeError);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("does nothing when item moved to same cell", () => {
|
|
||||||
const hotbarStore = HotbarStore.getInstance();
|
|
||||||
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
hotbarStore.restackItems(1, 1);
|
|
||||||
|
|
||||||
expect(hotbarStore.getActive().items[1].entity.uid).toEqual("test");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("new items takes first empty cell", () => {
|
|
||||||
const hotbarStore = HotbarStore.getInstance();
|
|
||||||
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
hotbarStore.addToHotbar(awsCluster);
|
|
||||||
hotbarStore.restackItems(0, 3);
|
|
||||||
hotbarStore.addToHotbar(minikubeCluster);
|
|
||||||
|
|
||||||
expect(hotbarStore.getActive().items[0].entity.uid).toEqual("minikube");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("throws if invalid arguments provided", () => {
|
|
||||||
// Prevent writing to stderr during this render.
|
|
||||||
const { error, warn } = console;
|
|
||||||
|
|
||||||
console.error = jest.fn();
|
|
||||||
console.warn = jest.fn();
|
|
||||||
|
|
||||||
const hotbarStore = HotbarStore.getInstance();
|
|
||||||
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
|
|
||||||
expect(() => hotbarStore.restackItems(-5, 0)).toThrow();
|
|
||||||
expect(() => hotbarStore.restackItems(2, -1)).toThrow();
|
|
||||||
expect(() => hotbarStore.restackItems(14, 1)).toThrow();
|
|
||||||
expect(() => hotbarStore.restackItems(11, 112)).toThrow();
|
|
||||||
|
|
||||||
// Restore writing to stderr.
|
|
||||||
console.error = error;
|
|
||||||
console.warn = warn;
|
|
||||||
});
|
|
||||||
|
|
||||||
it("checks if entity already pinned to hotbar", () => {
|
|
||||||
const hotbarStore = HotbarStore.getInstance();
|
|
||||||
|
|
||||||
hotbarStore.addToHotbar(testCluster);
|
|
||||||
|
|
||||||
expect(hotbarStore.isAddedToActive(testCluster)).toBeTruthy();
|
|
||||||
expect(hotbarStore.isAddedToActive(awsCluster)).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("pre beta-5 migrations", () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
const mockOpts = {
|
|
||||||
"some-directory-for-user-data": {
|
|
||||||
"lens-hotbar-store.json": JSON.stringify({
|
"lens-hotbar-store.json": JSON.stringify({
|
||||||
__internal__: {
|
__internal__: {
|
||||||
migrations: {
|
migrations: {
|
||||||
version: "5.0.0-beta.3",
|
version: "5.0.0-beta.3",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"hotbars": [
|
hotbars: [
|
||||||
{
|
{
|
||||||
"id": "3caac17f-aec2-4723-9694-ad204465d935",
|
id: "3caac17f-aec2-4723-9694-ad204465d935",
|
||||||
"name": "myhotbar",
|
name: "myhotbar",
|
||||||
"items": [
|
items: [
|
||||||
{
|
{
|
||||||
"entity": {
|
entity: {
|
||||||
"uid": "1dfa26e2ebab15780a3547e9c7fa785c",
|
uid: "1dfa26e2ebab15780a3547e9c7fa785c",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity": {
|
entity: {
|
||||||
"uid": "55b42c3c7ba3b04193416cda405269a5",
|
uid: "55b42c3c7ba3b04193416cda405269a5",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity": {
|
entity: {
|
||||||
"uid": "176fd331968660832f62283219d7eb6e",
|
uid: "176fd331968660832f62283219d7eb6e",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity": {
|
entity: {
|
||||||
"uid": "61c4fb45528840ebad1badc25da41d14",
|
uid: "61c4fb45528840ebad1badc25da41d14",
|
||||||
"name": "user1-context",
|
name: "user1-context",
|
||||||
"source": "local",
|
source: "local",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"entity": {
|
entity: {
|
||||||
"uid": "27d6f99fe9e7548a6e306760bfe19969",
|
uid: "27d6f99fe9e7548a6e306760bfe19969",
|
||||||
"name": "foo2",
|
name: "foo2",
|
||||||
"source": "local",
|
source: "local",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
"entity": {
|
entity: {
|
||||||
"uid": "c0b20040646849bb4dcf773e43a0bf27",
|
uid: "c0b20040646849bb4dcf773e43a0bf27",
|
||||||
"name": "multinode-demo",
|
name: "multinode-demo",
|
||||||
"source": "local",
|
source: "local",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
@ -399,15 +440,15 @@ describe("HotbarStore", () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
mockFs(mockOpts);
|
mockFs(configurationToBeMigrated);
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
await di.runSetups();
|
||||||
mockFs.restore();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("allows to retrieve a hotbar", () => {
|
it("allows to retrieve a hotbar", () => {
|
||||||
const hotbar = HotbarStore.getInstance().getById("3caac17f-aec2-4723-9694-ad204465d935");
|
const hotbar = HotbarStore.getInstance().getById(
|
||||||
|
"3caac17f-aec2-4723-9694-ad204465d935",
|
||||||
|
);
|
||||||
|
|
||||||
expect(hotbar.id).toBe("3caac17f-aec2-4723-9694-ad204465d935");
|
expect(hotbar.id).toBe("3caac17f-aec2-4723-9694-ad204465d935");
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2,16 +2,379 @@
|
|||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import { HotbarStore } from "./hotbar-store";
|
|
||||||
import catalogCatalogEntityInjectable from "./catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable";
|
import catalogCatalogEntityInjectable from "./catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable";
|
||||||
|
import { action, comparer, observable, makeObservable, computed } from "mobx";
|
||||||
|
import { BaseStore } from "./base-store";
|
||||||
|
import migrations from "../migrations/hotbar-store";
|
||||||
|
import { toJS } from "./utils";
|
||||||
|
import { CatalogEntity } from "./catalog";
|
||||||
|
import logger from "../main/logger";
|
||||||
|
import { broadcastMessage } from "./ipc";
|
||||||
|
import {
|
||||||
|
defaultHotbarCells,
|
||||||
|
getEmptyHotbar,
|
||||||
|
Hotbar,
|
||||||
|
CreateHotbarData,
|
||||||
|
CreateHotbarOptions,
|
||||||
|
} from "./hotbar-types";
|
||||||
|
import { hotbarTooManyItemsChannel } from "./ipc/hotbar";
|
||||||
|
import type { GeneralEntity } from "./catalog-entities";
|
||||||
|
|
||||||
|
export interface HotbarStoreModel {
|
||||||
|
hotbars: Hotbar[];
|
||||||
|
activeHotbarId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
catalogCatalogEntity: GeneralEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
||||||
|
readonly displayName = "HotbarStore";
|
||||||
|
@observable hotbars: Hotbar[] = [];
|
||||||
|
@observable private _activeHotbarId: string;
|
||||||
|
|
||||||
|
constructor(private dependencies: Dependencies) {
|
||||||
|
super({
|
||||||
|
configName: "lens-hotbar-store",
|
||||||
|
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
||||||
|
syncOptions: {
|
||||||
|
equals: comparer.structural,
|
||||||
|
},
|
||||||
|
migrations,
|
||||||
|
});
|
||||||
|
makeObservable(this);
|
||||||
|
this.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get activeHotbarId() {
|
||||||
|
return this._activeHotbarId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If `hotbar` points to a known hotbar, make it active. Otherwise, ignore
|
||||||
|
* @param hotbar The hotbar instance, or the index, or its ID
|
||||||
|
*/
|
||||||
|
setActiveHotbar(hotbar: Hotbar | number | string) {
|
||||||
|
if (typeof hotbar === "number") {
|
||||||
|
if (hotbar >= 0 && hotbar < this.hotbars.length) {
|
||||||
|
this._activeHotbarId = this.hotbars[hotbar].id;
|
||||||
|
}
|
||||||
|
} else if (typeof hotbar === "string") {
|
||||||
|
if (this.getById(hotbar)) {
|
||||||
|
this._activeHotbarId = hotbar;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.hotbars.indexOf(hotbar) >= 0) {
|
||||||
|
this._activeHotbarId = hotbar.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private hotbarIndexById(id: string) {
|
||||||
|
return this.hotbars.findIndex((hotbar) => hotbar.id === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private hotbarIndex(hotbar: Hotbar) {
|
||||||
|
return this.hotbars.indexOf(hotbar);
|
||||||
|
}
|
||||||
|
|
||||||
|
@computed get activeHotbarIndex() {
|
||||||
|
return this.hotbarIndexById(this.activeHotbarId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
protected fromStore(data: Partial<HotbarStoreModel> = {}) {
|
||||||
|
if (!data.hotbars || !data.hotbars.length) {
|
||||||
|
const hotbar = getEmptyHotbar("Default");
|
||||||
|
const {
|
||||||
|
metadata: { uid, name, source },
|
||||||
|
} = this.dependencies.catalogCatalogEntity;
|
||||||
|
const initialItem = { entity: { uid, name, source }};
|
||||||
|
|
||||||
|
hotbar.items[0] = initialItem;
|
||||||
|
|
||||||
|
this.hotbars = [hotbar];
|
||||||
|
} else {
|
||||||
|
this.hotbars = data.hotbars;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hotbars.forEach(ensureExactHotbarItemLength);
|
||||||
|
|
||||||
|
if (data.activeHotbarId) {
|
||||||
|
this.setActiveHotbar(data.activeHotbarId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.activeHotbarId) {
|
||||||
|
this.setActiveHotbar(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON(): HotbarStoreModel {
|
||||||
|
const model: HotbarStoreModel = {
|
||||||
|
hotbars: this.hotbars,
|
||||||
|
activeHotbarId: this.activeHotbarId,
|
||||||
|
};
|
||||||
|
|
||||||
|
return toJS(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
getActive() {
|
||||||
|
return this.getById(this.activeHotbarId);
|
||||||
|
}
|
||||||
|
|
||||||
|
getByName(name: string) {
|
||||||
|
return this.hotbars.find((hotbar) => hotbar.name === name);
|
||||||
|
}
|
||||||
|
|
||||||
|
getById(id: string) {
|
||||||
|
return this.hotbars.find((hotbar) => hotbar.id === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
add = action(
|
||||||
|
(
|
||||||
|
data: CreateHotbarData,
|
||||||
|
{ setActive = false }: CreateHotbarOptions = {},
|
||||||
|
) => {
|
||||||
|
const hotbar = getEmptyHotbar(data.name, data.id);
|
||||||
|
|
||||||
|
this.hotbars.push(hotbar);
|
||||||
|
|
||||||
|
if (setActive) {
|
||||||
|
this._activeHotbarId = hotbar.id;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
setHotbarName = action((id: string, name: string) => {
|
||||||
|
const index = this.hotbars.findIndex((hotbar) => hotbar.id === id);
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
return void console.warn(
|
||||||
|
`[HOTBAR-STORE]: cannot setHotbarName: unknown id`,
|
||||||
|
{ id },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hotbars[index].name = name;
|
||||||
|
});
|
||||||
|
|
||||||
|
remove = action((hotbar: Hotbar) => {
|
||||||
|
if (this.hotbars.length <= 1) {
|
||||||
|
throw new Error("Cannot remove the last hotbar");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hotbars = this.hotbars.filter((h) => h !== hotbar);
|
||||||
|
|
||||||
|
if (this.activeHotbarId === hotbar.id) {
|
||||||
|
this.setActiveHotbar(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
@action
|
||||||
|
addToHotbar(item: CatalogEntity, cellIndex?: number) {
|
||||||
|
const hotbar = this.getActive();
|
||||||
|
const uid = item.getId();
|
||||||
|
const name = item.getName();
|
||||||
|
|
||||||
|
if (typeof uid !== "string") {
|
||||||
|
throw new TypeError("CatalogEntity's ID must be a string");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof name !== "string") {
|
||||||
|
throw new TypeError("CatalogEntity's NAME must be a string");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isAddedToActive(item)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const entity = {
|
||||||
|
uid,
|
||||||
|
name,
|
||||||
|
source: item.metadata.source,
|
||||||
|
};
|
||||||
|
const newItem = { entity };
|
||||||
|
|
||||||
|
if (cellIndex === undefined) {
|
||||||
|
// Add item to empty cell
|
||||||
|
const emptyCellIndex = hotbar.items.indexOf(null);
|
||||||
|
|
||||||
|
if (emptyCellIndex != -1) {
|
||||||
|
hotbar.items[emptyCellIndex] = newItem;
|
||||||
|
} else {
|
||||||
|
broadcastMessage(hotbarTooManyItemsChannel);
|
||||||
|
}
|
||||||
|
} else if (0 <= cellIndex && cellIndex < hotbar.items.length) {
|
||||||
|
hotbar.items[cellIndex] = newItem;
|
||||||
|
} else {
|
||||||
|
logger.error(
|
||||||
|
`[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range`,
|
||||||
|
{ entityId: uid, hotbarId: hotbar.id, cellIndex },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
removeFromHotbar(uid: string): void {
|
||||||
|
const hotbar = this.getActive();
|
||||||
|
const index = hotbar.items.findIndex((item) => item?.entity.uid === uid);
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hotbar.items[index] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all hotbar items that reference the `uid`.
|
||||||
|
* @param uid The `EntityId` that each hotbar item refers to
|
||||||
|
* @returns A function that will (in an action) undo the removing of the hotbar items. This function will not complete if the hotbar has changed.
|
||||||
|
*/
|
||||||
|
@action
|
||||||
|
removeAllHotbarItems(uid: string) {
|
||||||
|
for (const hotbar of this.hotbars) {
|
||||||
|
const index = hotbar.items.findIndex((i) => i?.entity.uid === uid);
|
||||||
|
|
||||||
|
if (index >= 0) {
|
||||||
|
hotbar.items[index] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
findClosestEmptyIndex(from: number, direction = 1) {
|
||||||
|
let index = from;
|
||||||
|
|
||||||
|
while (this.getActive().items[index] != null) {
|
||||||
|
index += direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
restackItems(from: number, to: number): void {
|
||||||
|
const { items } = this.getActive();
|
||||||
|
const source = items[from];
|
||||||
|
const moveDown = from < to;
|
||||||
|
|
||||||
|
if (
|
||||||
|
from < 0 ||
|
||||||
|
to < 0 ||
|
||||||
|
from >= items.length ||
|
||||||
|
to >= items.length ||
|
||||||
|
isNaN(from) ||
|
||||||
|
isNaN(to)
|
||||||
|
) {
|
||||||
|
throw new Error("Invalid 'from' or 'to' arguments");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (from == to) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
items.splice(from, 1, null);
|
||||||
|
|
||||||
|
if (items[to] == null) {
|
||||||
|
items.splice(to, 1, source);
|
||||||
|
} else {
|
||||||
|
// Move cells up or down to closes empty cell
|
||||||
|
items.splice(this.findClosestEmptyIndex(to, moveDown ? -1 : 1), 1);
|
||||||
|
items.splice(to, 0, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switchToPrevious() {
|
||||||
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
let index = hotbarStore.activeHotbarIndex - 1;
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
index = hotbarStore.hotbars.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
hotbarStore.setActiveHotbar(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
switchToNext() {
|
||||||
|
const hotbarStore = HotbarStore.getInstance();
|
||||||
|
let index = hotbarStore.activeHotbarIndex + 1;
|
||||||
|
|
||||||
|
if (index >= hotbarStore.hotbars.length) {
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hotbarStore.setActiveHotbar(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if entity already pinned to the active hotbar
|
||||||
|
*/
|
||||||
|
isAddedToActive(entity: CatalogEntity | null | undefined): boolean {
|
||||||
|
if (!entity) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
this.getActive().items.findIndex(
|
||||||
|
(item) => item?.entity.uid === entity.getId(),
|
||||||
|
) >= 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDisplayLabel(hotbar: Hotbar): string {
|
||||||
|
return `${this.getDisplayIndex(hotbar)}: ${hotbar.name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDisplayIndex(hotbar: Hotbar): string {
|
||||||
|
const index = this.hotbarIndex(hotbar);
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
return "??";
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${index + 1}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function ensures that there are always exactly `defaultHotbarCells`
|
||||||
|
* worth of items in the hotbar.
|
||||||
|
* @param hotbar The hotbar to modify
|
||||||
|
*/
|
||||||
|
function ensureExactHotbarItemLength(hotbar: Hotbar) {
|
||||||
|
// if there are not enough items
|
||||||
|
while (hotbar.items.length < defaultHotbarCells) {
|
||||||
|
hotbar.items.push(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if for some reason the hotbar was overfilled before, remove as many entries
|
||||||
|
// as needed, but prefer empty slots and items at the end first.
|
||||||
|
while (hotbar.items.length > defaultHotbarCells) {
|
||||||
|
const lastNull = hotbar.items.lastIndexOf(null);
|
||||||
|
|
||||||
|
if (lastNull >= 0) {
|
||||||
|
hotbar.items.splice(lastNull, 1);
|
||||||
|
} else {
|
||||||
|
hotbar.items.length = defaultHotbarCells;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const hotbarStoreInjectable = getInjectable({
|
const hotbarStoreInjectable = getInjectable({
|
||||||
id: "hotbar-store",
|
id: "hotbar-store",
|
||||||
|
|
||||||
instantiate: (di) => HotbarStore.createInstance({
|
instantiate: (di) => {
|
||||||
catalogCatalogEntity: di.inject(catalogCatalogEntityInjectable),
|
HotbarStore.resetInstance();
|
||||||
}),
|
|
||||||
|
const instance = HotbarStore.createInstance({
|
||||||
|
catalogCatalogEntity: di.inject(catalogCatalogEntityInjectable),
|
||||||
|
});
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
},
|
||||||
|
|
||||||
causesSideEffects: true,
|
causesSideEffects: true,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,330 +3,10 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { action, comparer, observable, makeObservable, computed } from "mobx";
|
import hotbarStoreInjectable from "./hotbar-store.injectable";
|
||||||
import { BaseStore } from "./base-store";
|
|
||||||
import migrations from "../migrations/hotbar-store";
|
|
||||||
import { toJS } from "./utils";
|
|
||||||
import { CatalogEntity } from "./catalog";
|
|
||||||
import logger from "../main/logger";
|
|
||||||
import { broadcastMessage } from "./ipc";
|
|
||||||
import { defaultHotbarCells, getEmptyHotbar, Hotbar, CreateHotbarData, CreateHotbarOptions } from "./hotbar-types";
|
|
||||||
import { hotbarTooManyItemsChannel } from "./ipc/hotbar";
|
|
||||||
import type { GeneralEntity } from "./catalog-entities";
|
|
||||||
|
|
||||||
export interface HotbarStoreModel {
|
import {
|
||||||
hotbars: Hotbar[];
|
asLegacyGlobalSingletonForExtensionApi,
|
||||||
activeHotbarId: string;
|
} from "../extensions/as-legacy-globals-for-extension-api/as-legacy-global-singleton-object-for-extension-api";
|
||||||
}
|
|
||||||
|
|
||||||
interface Dependencies {
|
export const HotbarStore = asLegacyGlobalSingletonForExtensionApi(hotbarStoreInjectable);
|
||||||
catalogCatalogEntity: GeneralEntity;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class HotbarStore extends BaseStore<HotbarStoreModel> {
|
|
||||||
readonly displayName = "HotbarStore";
|
|
||||||
@observable hotbars: Hotbar[] = [];
|
|
||||||
@observable private _activeHotbarId: string;
|
|
||||||
|
|
||||||
constructor(private dependencies: Dependencies) {
|
|
||||||
super({
|
|
||||||
configName: "lens-hotbar-store",
|
|
||||||
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
|
||||||
syncOptions: {
|
|
||||||
equals: comparer.structural,
|
|
||||||
},
|
|
||||||
migrations,
|
|
||||||
});
|
|
||||||
makeObservable(this);
|
|
||||||
this.load();
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed get activeHotbarId() {
|
|
||||||
return this._activeHotbarId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If `hotbar` points to a known hotbar, make it active. Otherwise, ignore
|
|
||||||
* @param hotbar The hotbar instance, or the index, or its ID
|
|
||||||
*/
|
|
||||||
setActiveHotbar(hotbar: Hotbar | number | string) {
|
|
||||||
if (typeof hotbar === "number") {
|
|
||||||
if (hotbar >= 0 && hotbar < this.hotbars.length) {
|
|
||||||
this._activeHotbarId = this.hotbars[hotbar].id;
|
|
||||||
}
|
|
||||||
} else if (typeof hotbar === "string") {
|
|
||||||
if (this.getById(hotbar)) {
|
|
||||||
this._activeHotbarId = hotbar;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (this.hotbars.indexOf(hotbar) >= 0) {
|
|
||||||
this._activeHotbarId = hotbar.id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private hotbarIndexById(id: string) {
|
|
||||||
return this.hotbars.findIndex((hotbar) => hotbar.id === id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private hotbarIndex(hotbar: Hotbar) {
|
|
||||||
return this.hotbars.indexOf(hotbar);
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed get activeHotbarIndex() {
|
|
||||||
return this.hotbarIndexById(this.activeHotbarId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
protected fromStore(data: Partial<HotbarStoreModel> = {}) {
|
|
||||||
if (!data.hotbars || !data.hotbars.length) {
|
|
||||||
const hotbar = getEmptyHotbar("Default");
|
|
||||||
const { metadata: { uid, name, source }} = this.dependencies.catalogCatalogEntity;
|
|
||||||
const initialItem = { entity: { uid, name, source }};
|
|
||||||
|
|
||||||
hotbar.items[0] = initialItem;
|
|
||||||
|
|
||||||
this.hotbars = [hotbar];
|
|
||||||
} else {
|
|
||||||
this.hotbars = data.hotbars;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hotbars.forEach(ensureExactHotbarItemLength);
|
|
||||||
|
|
||||||
if (data.activeHotbarId) {
|
|
||||||
this.setActiveHotbar(data.activeHotbarId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.activeHotbarId) {
|
|
||||||
this.setActiveHotbar(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toJSON(): HotbarStoreModel {
|
|
||||||
const model: HotbarStoreModel = {
|
|
||||||
hotbars: this.hotbars,
|
|
||||||
activeHotbarId: this.activeHotbarId,
|
|
||||||
};
|
|
||||||
|
|
||||||
return toJS(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
getActive() {
|
|
||||||
return this.getById(this.activeHotbarId);
|
|
||||||
}
|
|
||||||
|
|
||||||
getByName(name: string) {
|
|
||||||
return this.hotbars.find((hotbar) => hotbar.name === name);
|
|
||||||
}
|
|
||||||
|
|
||||||
getById(id: string) {
|
|
||||||
return this.hotbars.find((hotbar) => hotbar.id === id);
|
|
||||||
}
|
|
||||||
|
|
||||||
add = action((data: CreateHotbarData, { setActive = false }: CreateHotbarOptions = {}) => {
|
|
||||||
const hotbar = getEmptyHotbar(data.name, data.id);
|
|
||||||
|
|
||||||
this.hotbars.push(hotbar);
|
|
||||||
|
|
||||||
if (setActive) {
|
|
||||||
this._activeHotbarId = hotbar.id;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setHotbarName = action((id: string, name: string) => {
|
|
||||||
const index = this.hotbars.findIndex((hotbar) => hotbar.id === id);
|
|
||||||
|
|
||||||
if (index < 0) {
|
|
||||||
return void console.warn(`[HOTBAR-STORE]: cannot setHotbarName: unknown id`, { id });
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hotbars[index].name = name;
|
|
||||||
});
|
|
||||||
|
|
||||||
remove = action((hotbar: Hotbar) => {
|
|
||||||
if (this.hotbars.length <= 1) {
|
|
||||||
throw new Error("Cannot remove the last hotbar");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.hotbars = this.hotbars.filter((h) => h !== hotbar);
|
|
||||||
|
|
||||||
if (this.activeHotbarId === hotbar.id) {
|
|
||||||
this.setActiveHotbar(0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
@action
|
|
||||||
addToHotbar(item: CatalogEntity, cellIndex?: number) {
|
|
||||||
const hotbar = this.getActive();
|
|
||||||
const uid = item.getId();
|
|
||||||
const name = item.getName();
|
|
||||||
|
|
||||||
if (typeof uid !== "string") {
|
|
||||||
throw new TypeError("CatalogEntity's ID must be a string");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof name !== "string") {
|
|
||||||
throw new TypeError("CatalogEntity's NAME must be a string");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isAddedToActive(item)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const entity = {
|
|
||||||
uid,
|
|
||||||
name,
|
|
||||||
source: item.metadata.source,
|
|
||||||
};
|
|
||||||
const newItem = { entity };
|
|
||||||
|
|
||||||
if (cellIndex === undefined) {
|
|
||||||
// Add item to empty cell
|
|
||||||
const emptyCellIndex = hotbar.items.indexOf(null);
|
|
||||||
|
|
||||||
if (emptyCellIndex != -1) {
|
|
||||||
hotbar.items[emptyCellIndex] = newItem;
|
|
||||||
} else {
|
|
||||||
broadcastMessage(hotbarTooManyItemsChannel);
|
|
||||||
}
|
|
||||||
} else if (0 <= cellIndex && cellIndex < hotbar.items.length) {
|
|
||||||
hotbar.items[cellIndex] = newItem;
|
|
||||||
} else {
|
|
||||||
logger.error(`[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range`, { entityId: uid, hotbarId: hotbar.id, cellIndex });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
removeFromHotbar(uid: string): void {
|
|
||||||
const hotbar = this.getActive();
|
|
||||||
const index = hotbar.items.findIndex(item => item?.entity.uid === uid);
|
|
||||||
|
|
||||||
if (index < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hotbar.items[index] = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all hotbar items that reference the `uid`.
|
|
||||||
* @param uid The `EntityId` that each hotbar item refers to
|
|
||||||
* @returns A function that will (in an action) undo the removing of the hotbar items. This function will not complete if the hotbar has changed.
|
|
||||||
*/
|
|
||||||
@action
|
|
||||||
removeAllHotbarItems(uid: string) {
|
|
||||||
for (const hotbar of this.hotbars) {
|
|
||||||
const index = hotbar.items.findIndex((i) => i?.entity.uid === uid);
|
|
||||||
|
|
||||||
if (index >= 0) {
|
|
||||||
hotbar.items[index] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
findClosestEmptyIndex(from: number, direction = 1) {
|
|
||||||
let index = from;
|
|
||||||
|
|
||||||
while(this.getActive().items[index] != null) {
|
|
||||||
index += direction;
|
|
||||||
}
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
restackItems(from: number, to: number): void {
|
|
||||||
const { items } = this.getActive();
|
|
||||||
const source = items[from];
|
|
||||||
const moveDown = from < to;
|
|
||||||
|
|
||||||
if (from < 0 || to < 0 || from >= items.length || to >= items.length || isNaN(from) || isNaN(to)) {
|
|
||||||
throw new Error("Invalid 'from' or 'to' arguments");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (from == to) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
items.splice(from, 1, null);
|
|
||||||
|
|
||||||
if (items[to] == null) {
|
|
||||||
items.splice(to, 1, source);
|
|
||||||
} else {
|
|
||||||
// Move cells up or down to closes empty cell
|
|
||||||
items.splice(this.findClosestEmptyIndex(to, moveDown ? -1 : 1), 1);
|
|
||||||
items.splice(to, 0, source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switchToPrevious() {
|
|
||||||
const hotbarStore = HotbarStore.getInstance();
|
|
||||||
let index = hotbarStore.activeHotbarIndex - 1;
|
|
||||||
|
|
||||||
if (index < 0) {
|
|
||||||
index = hotbarStore.hotbars.length - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
hotbarStore.setActiveHotbar(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
switchToNext() {
|
|
||||||
const hotbarStore = HotbarStore.getInstance();
|
|
||||||
let index = hotbarStore.activeHotbarIndex + 1;
|
|
||||||
|
|
||||||
if (index >= hotbarStore.hotbars.length) {
|
|
||||||
index = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
hotbarStore.setActiveHotbar(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if entity already pinned to the active hotbar
|
|
||||||
*/
|
|
||||||
isAddedToActive(entity: CatalogEntity | null | undefined): boolean {
|
|
||||||
if (!entity) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.getActive().items.findIndex(item => item?.entity.uid === entity.getId()) >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
getDisplayLabel(hotbar: Hotbar): string {
|
|
||||||
return `${this.getDisplayIndex(hotbar)}: ${hotbar.name}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
getDisplayIndex(hotbar: Hotbar): string {
|
|
||||||
const index = this.hotbarIndex(hotbar);
|
|
||||||
|
|
||||||
if (index < 0) {
|
|
||||||
return "??";
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${index + 1}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function ensures that there are always exactly `defaultHotbarCells`
|
|
||||||
* worth of items in the hotbar.
|
|
||||||
* @param hotbar The hotbar to modify
|
|
||||||
*/
|
|
||||||
function ensureExactHotbarItemLength(hotbar: Hotbar) {
|
|
||||||
// if there are not enough items
|
|
||||||
while (hotbar.items.length < defaultHotbarCells) {
|
|
||||||
hotbar.items.push(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if for some reason the hotbar was overfilled before, remove as many entries
|
|
||||||
// as needed, but prefer empty slots and items at the end first.
|
|
||||||
while (hotbar.items.length > defaultHotbarCells) {
|
|
||||||
const lastNull = hotbar.items.lastIndexOf(null);
|
|
||||||
|
|
||||||
if (lastNull >= 0) {
|
|
||||||
hotbar.items.splice(lastNull, 1);
|
|
||||||
} else {
|
|
||||||
hotbar.items.length = defaultHotbarCells;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user