diff --git a/package.json b/package.json index d17c26f248..42cbddc6bf 100644 --- a/package.json +++ b/package.json @@ -315,7 +315,7 @@ "@testing-library/dom": "^7.31.2", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^12.1.5", - "@testing-library/user-event": "^13.5.0", + "@testing-library/user-event": "^14.4.3", "@types/byline": "^4.2.33", "@types/chart.js": "^2.9.36", "@types/circular-dependency-plugin": "5.0.5", diff --git a/src/common/__tests__/cluster-store.test.ts b/src/common/__tests__/cluster-store.test.ts index 9dd1a29a5b..2fb52df3ba 100644 --- a/src/common/__tests__/cluster-store.test.ts +++ b/src/common/__tests__/cluster-store.test.ts @@ -17,7 +17,6 @@ import directoryForTempInjectable from "../app-paths/directory-for-temp/director import kubectlBinaryNameInjectable from "../../main/kubectl/binary-name.injectable"; import kubectlDownloadingNormalizedArchInjectable from "../../main/kubectl/normalized-arch.injectable"; import normalizedPlatformInjectable from "../vars/normalized-platform.injectable"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; import type { WriteJsonSync } from "../fs/write-json-sync.injectable"; import writeJsonSyncInjectable from "../fs/write-json-sync.injectable"; import type { ReadFileSync } from "../fs/read-file-sync.injectable"; @@ -27,6 +26,7 @@ import type { WriteFileSync } from "../fs/write-file-sync.injectable"; import writeFileSyncInjectable from "../fs/write-file-sync.injectable"; import type { WriteBufferSync } from "../fs/write-buffer-sync.injectable"; import writeBufferSyncInjectable from "../fs/write-buffer-sync.injectable"; +import clusterStoreMigrationVersionInjectable from "../cluster-store/migration-version.injectable"; // NOTE: this is intended to read the actual file system const testDataIcon = readFileSync("test-data/cluster-store-migration-icon.png"); @@ -281,7 +281,7 @@ describe("cluster-store", () => { }); writeBufferSync("/some-directory-for-user-data/icon_path", testDataIcon); - di.override(storeMigrationVersionInjectable, () => "3.6.0"); + di.override(clusterStoreMigrationVersionInjectable, () => "3.6.0"); clusterStore = di.inject(clusterStoreInjectable); clusterStore.load(); diff --git a/src/common/__tests__/hotbar-store.test.ts b/src/common/__tests__/hotbar-store.test.ts index ac8cecc1d2..9143aa6e6b 100644 --- a/src/common/__tests__/hotbar-store.test.ts +++ b/src/common/__tests__/hotbar-store.test.ts @@ -16,8 +16,8 @@ import catalogCatalogEntityInjectable from "../catalog-entities/general-catalog- import loggerInjectable from "../logger.injectable"; import type { Logger } from "../logger"; import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; import writeJsonSyncInjectable from "../fs/write-json-sync.injectable"; +import hotbarStoreMigrationVersionInjectable from "../hotbars/migration-version.injectable"; function getMockCatalogEntity(data: Partial & CatalogEntityKindData): CatalogEntity { return { @@ -260,7 +260,7 @@ describe("HotbarStore", () => { }); }); - describe("given data from 5.0.0-beta.3 and version being 5.0.0-beta.10", () => { + describe("given data from 5.0.0-beta.3 and version being 5.6.0-alpha.7", () => { beforeEach(() => { const writeJsonSync = di.inject(writeJsonSyncInjectable); @@ -322,7 +322,7 @@ describe("HotbarStore", () => { ], }); - di.override(storeMigrationVersionInjectable, () => "5.0.0-beta.10"); + di.override(hotbarStoreMigrationVersionInjectable, () => "5.6.0-alpha.7"); hotbarStore = di.inject(hotbarStoreInjectable); @@ -349,6 +349,7 @@ describe("HotbarStore", () => { name: "my-aws-cluster", source: "local", uid: "some-aws-id", + shortName: "mac", }, }); }); diff --git a/src/common/__tests__/user-store.test.ts b/src/common/__tests__/user-store.test.ts index 7071fc5c17..abd389d2cf 100644 --- a/src/common/__tests__/user-store.test.ts +++ b/src/common/__tests__/user-store.test.ts @@ -10,11 +10,11 @@ import type { ClusterStoreModel } from "../cluster-store/cluster-store"; import { defaultThemeId } from "../vars"; import writeFileInjectable from "../fs/write-file.injectable"; import { getDiForUnitTesting } from "../../main/getDiForUnitTesting"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; import releaseChannelInjectable from "../vars/release-channel.injectable"; import defaultUpdateChannelInjectable from "../../features/application-update/common/selected-update-channel/default-update-channel.injectable"; import writeJsonSyncInjectable from "../fs/write-json-sync.injectable"; import writeFileSyncInjectable from "../fs/write-file-sync.injectable"; +import userStoreMigrationVersionInjectable from "../user-store/migration-version.injectable"; describe("user store tests", () => { let userStore: UserStore; @@ -88,7 +88,7 @@ describe("user store tests", () => { writeFileSync("/some/other/path", "is file"); - di.override(storeMigrationVersionInjectable, () => "10.0.0"); + di.override(userStoreMigrationVersionInjectable, () => "10.0.0"); userStore.load(); }); diff --git a/src/common/base-store/base-store.ts b/src/common/base-store/base-store.ts index 9a786329e4..0f310dadf5 100644 --- a/src/common/base-store/base-store.ts +++ b/src/common/base-store/base-store.ts @@ -8,7 +8,6 @@ import type { Migrations, Options as ConfOptions } from "conf/dist/source/types" import type { IEqualsComparer } from "mobx"; import { makeObservable, reaction } from "mobx"; import { disposer, isPromiseLike, toJS } from "../utils"; -import { broadcastMessage } from "../ipc"; import isEqual from "lodash/isEqual"; import { kebabCase } from "lodash"; import type { GetConfigurationFileModel } from "../get-configuration-file-model/get-configuration-file-model.injectable"; @@ -16,18 +15,20 @@ import type { Logger } from "../logger"; import type { PersistStateToConfig } from "./save-to-file"; import type { GetBasenameOfPath } from "../path/get-basename.injectable"; import type { EnlistMessageChannelListener } from "../utils/channel/enlist-message-channel-listener-injection-token"; +import type { SendMessageToChannel } from "../utils/channel/message-to-channel-injection-token"; +import type { MessageChannel } from "../utils/channel/message-channel-listener-injection-token"; -export interface BaseStoreParams extends Omit, "migrations"> { +export interface BaseStoreParams extends Omit, "migrations" | "projectVersion"> { syncOptions?: { fireImmediately?: boolean; equals?: IEqualsComparer; }; - configName: string; + readonly configName: string; } export interface IpcChannelPrefixes { - local: string; - remote: string; + readonly local: string; + readonly remote: string; } export interface BaseStoreDependencies { @@ -41,6 +42,7 @@ export interface BaseStoreDependencies { persistStateToConfig: PersistStateToConfig; getBasenameOfPath: GetBasenameOfPath; enlistMessageChannelListener: EnlistMessageChannelListener; + sendMessageToChannel: SendMessageToChannel; } /** @@ -90,20 +92,26 @@ export abstract class BaseStore { const name = this.dependencies.getBasenameOfPath(config.path); const disableSync = () => this.syncDisposers(); + + const sendChannel: MessageChannel = { + id: `${this.dependencies.ipcChannelPrefixes.remote}:${config.path}`, + }; + const receiveChannel: MessageChannel = { + id: `${this.dependencies.ipcChannelPrefixes.local}:${config.path}`, + }; + const enableSync = () => { this.syncDisposers.push( reaction( () => toJS(this.toJSON()), // unwrap possible observables and react to everything model => { this.dependencies.persistStateToConfig(config, model); - broadcastMessage(`${this.dependencies.ipcChannelPrefixes.remote}:${config.path}`, model); + this.dependencies.sendMessageToChannel(sendChannel, model); }, this.params.syncOptions, ), this.dependencies.enlistMessageChannelListener({ - channel: { - id: `${this.dependencies.ipcChannelPrefixes.local}:${config.path}`, - }, + channel: receiveChannel, handler: (model) => { this.dependencies.logger.silly(`[${this.displayName}]: syncing ${name}`, { model }); @@ -113,11 +121,13 @@ export abstract class BaseStore { // todo: use "resourceVersion" if merge required (to avoid equality checks => better performance) if (!isEqual(this.toJSON(), model)) { - this.fromStore(model as T); + this.fromStore(model); } if (this.dependencies.shouldDisableSyncInListener) { - enableSync(); + setImmediate(() => { + enableSync(); + }); } }, }), diff --git a/src/common/catalog-entities/kubernetes-cluster.ts b/src/common/catalog-entities/kubernetes-cluster.ts index 57cac07122..e3993fa9a3 100644 --- a/src/common/catalog-entities/kubernetes-cluster.ts +++ b/src/common/catalog-entities/kubernetes-cluster.ts @@ -97,7 +97,7 @@ export class KubernetesCluster< } } - async onRun(context: CatalogEntityActionContext) { + onRun(context: CatalogEntityActionContext) { context.navigate(`/cluster/${this.getId()}`); } diff --git a/src/common/catalog-entities/web-link.ts b/src/common/catalog-entities/web-link.ts index 7c83051c8b..372e90deb9 100644 --- a/src/common/catalog-entities/web-link.ts +++ b/src/common/catalog-entities/web-link.ts @@ -4,7 +4,7 @@ */ import { Environments, getEnvironmentSpecificLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import type { CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog"; +import type { CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntitySpec, CatalogEntityStatus } from "../catalog"; import { CatalogCategory, CatalogEntity, categoryVersion } from "../catalog/catalog-entity"; import productNameInjectable from "../vars/product-name.injectable"; import weblinkStoreInjectable from "../weblinks-store/weblink-store.injectable"; @@ -15,7 +15,7 @@ export interface WebLinkStatus extends CatalogEntityStatus { phase: WebLinkStatusPhase; } -export interface WebLinkSpec { +export interface WebLinkSpec extends CatalogEntitySpec { url: string; } @@ -23,10 +23,10 @@ export class WebLink extends CatalogEntity { + describe("computeDefaultShortName", () => { + it.each([ + ["a", "a"], + ["", "??"], + [1, "??"], + [true, "??"], + ["ab", "ab"], + ["abc", "ab"], + ["abcde", "ab"], + ["ab-cd", "ac"], + ["ab-cd la", "al"], + ["ab-cd la_1", "al"], + ["ab-cd la 1_3", "al1"], + ["ab-cd la 1_3 lk", "al1"], + ["ab-cd la 1_3 lk aj", "al1"], + ["😀 a", "😀a"], + ["😀😎 a", "😀a"], + ["🇫🇮 Finland", "🇫🇮F"], + ["إعجم", "إع"], + ])("should compute from %p into %p", (input: any, output: string) => { + expect(computeDefaultShortName(input)).toBe(output); + }); + }); +}); diff --git a/src/common/catalog/catalog-entity.ts b/src/common/catalog/catalog-entity.ts index 7effe60f2f..52da511560 100644 --- a/src/common/catalog/catalog-entity.ts +++ b/src/common/catalog/catalog-entity.ts @@ -321,7 +321,7 @@ export interface CatalogEntityAddMenuContext { menuItems: CatalogEntityAddMenu[]; } -export type CatalogEntitySpec = Record; +export type CatalogEntitySpec = Partial>; export interface CatalogEntityData< @@ -359,8 +359,6 @@ export abstract class CatalogEntity< @observable spec: Spec; constructor({ metadata, status, spec }: CatalogEntityData) { - makeObservable(this); - if (!metadata || typeof metadata !== "object") { throw new TypeError("CatalogEntity's metadata must be a defined object"); } @@ -376,6 +374,8 @@ export abstract class CatalogEntity< this.metadata = metadata; this.status = status; this.spec = spec; + + makeObservable(this); } /** diff --git a/src/common/catalog/helpers.ts b/src/common/catalog/helpers.ts new file mode 100644 index 0000000000..c1115ac392 --- /dev/null +++ b/src/common/catalog/helpers.ts @@ -0,0 +1,83 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { CatalogEntity } from "./catalog-entity"; +import GraphemeSplitter from "grapheme-splitter"; +import { hasOwnProperty, hasTypedProperty, isObject, isString, iter } from "../utils"; + +function getNameParts(name: string): string[] { + const byWhitespace = name.split(/\s+/); + + if (byWhitespace.length > 1) { + return byWhitespace; + } + + const byDashes = name.split(/[-_]+/); + + if (byDashes.length > 1) { + return byDashes; + } + + return name.split(/@+/); +} + +export function limitGraphemeLengthOf(src: string, count: number): string { + const splitter = new GraphemeSplitter(); + + return iter + .chain(splitter.iterateGraphemes(src)) + .take(count) + .join(""); +} + +export function computeDefaultShortName(name: string) { + if (!name || typeof name !== "string") { + return "??"; + } + + const [rawFirst, rawSecond, rawThird] = getNameParts(name); + const splitter = new GraphemeSplitter(); + const first = splitter.iterateGraphemes(rawFirst); + const second = rawSecond ? splitter.iterateGraphemes(rawSecond): first; + const third = rawThird ? splitter.iterateGraphemes(rawThird) : iter.newEmpty(); + + return iter.chain(iter.take(first, 1)) + .concat(iter.take(second, 1)) + .concat(iter.take(third, 1)) + .join(""); +} + +export function getShortName(entity: CatalogEntity): string { + return entity.metadata.shortName || computeDefaultShortName(entity.getName()); +} + +export function getIconColourHash(entity: CatalogEntity): string { + return `${entity.metadata.name}-${entity.metadata.source}`; +} + +export function getIconBackground(entity: CatalogEntity): string | undefined { + if (isObject(entity.spec.icon)) { + if (hasTypedProperty(entity.spec.icon, "background", isString)) { + return entity.spec.icon.background; + } + + return hasOwnProperty(entity.spec.icon, "src") + ? "transparent" + : undefined; + } + + return undefined; +} + +export function getIconMaterial(entity: CatalogEntity): string | undefined { + if ( + isObject(entity.spec.icon) + && hasTypedProperty(entity.spec.icon, "material", isString) + ) { + return entity.spec.icon.material; + } + + return undefined; +} diff --git a/src/common/cluster-store/cluster-store.injectable.ts b/src/common/cluster-store/cluster-store.injectable.ts index 9712e3fdb0..3dd7b6d92b 100644 --- a/src/common/cluster-store/cluster-store.injectable.ts +++ b/src/common/cluster-store/cluster-store.injectable.ts @@ -10,7 +10,6 @@ import emitAppEventInjectable from "../app-event-bus/emit-event.injectable"; import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; import loggerInjectable from "../logger.injectable"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; import storeMigrationsInjectable from "../base-store/migrations.injectable"; import { clusterStoreMigrationInjectionToken } from "./migration-token"; import { baseStoreIpcChannelPrefixesInjectionToken } from "../base-store/channel-prefix"; @@ -18,6 +17,8 @@ import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../base-s import { persistStateToConfigInjectionToken } from "../base-store/save-to-file"; import getBasenameOfPathInjectable from "../path/get-basename.injectable"; import { enlistMessageChannelListenerInjectionToken } from "../utils/channel/enlist-message-channel-listener-injection-token"; +import clusterStoreMigrationVersionInjectable from "./migration-version.injectable"; +import { sendMessageToChannelInjectionToken } from "../utils/channel/message-to-channel-injection-token"; const clusterStoreInjectable = getInjectable({ id: "cluster-store", @@ -29,13 +30,14 @@ const clusterStoreInjectable = getInjectable({ directoryForUserData: di.inject(directoryForUserDataInjectable), getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), logger: di.inject(loggerInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), + storeMigrationVersion: di.inject(clusterStoreMigrationVersionInjectable), migrations: di.inject(storeMigrationsInjectable, clusterStoreMigrationInjectionToken), getBasenameOfPath: di.inject(getBasenameOfPathInjectable), ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), persistStateToConfig: di.inject(persistStateToConfigInjectionToken), enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), + sendMessageToChannel: di.inject(sendMessageToChannelInjectionToken), }), }); diff --git a/src/common/cluster-store/migration-version.injectable.ts b/src/common/cluster-store/migration-version.injectable.ts new file mode 100644 index 0000000000..cf096692c4 --- /dev/null +++ b/src/common/cluster-store/migration-version.injectable.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; + +const clusterStoreMigrationVersionInjectable = getInjectable({ + id: "cluster-store-migration-version", + instantiate: () => "6.4.0", +}); + +export default clusterStoreMigrationVersionInjectable; diff --git a/src/common/entity-preferences/get-shortname.injectable.ts b/src/common/entity-preferences/get-shortname.injectable.ts new file mode 100644 index 0000000000..753f76e46a --- /dev/null +++ b/src/common/entity-preferences/get-shortname.injectable.ts @@ -0,0 +1,17 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import entityPreferencesStoreInjectable from "./store.injectable"; + +const getEntityShortnameInjectable = getInjectable({ + id: "get-entity-shortname", + instantiate: (di) => { + const entityPreferencesStore = di.inject(entityPreferencesStoreInjectable); + + return (uid: string) => entityPreferencesStore.preferences.get(uid)?.shortName; + }, +}); + +export default getEntityShortnameInjectable; diff --git a/src/common/entity-preferences/migration-version.injectable.ts b/src/common/entity-preferences/migration-version.injectable.ts new file mode 100644 index 0000000000..83d9d8c0cf --- /dev/null +++ b/src/common/entity-preferences/migration-version.injectable.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; + +const entityPreferencesStoreMigrationVersionInjectable = getInjectable({ + id: "entity-preferences-store-migration-version", + instantiate: () => "6.4.0", +}); + +export default entityPreferencesStoreMigrationVersionInjectable; diff --git a/src/common/entity-preferences/store.injectable.ts b/src/common/entity-preferences/store.injectable.ts new file mode 100644 index 0000000000..4525472a69 --- /dev/null +++ b/src/common/entity-preferences/store.injectable.ts @@ -0,0 +1,35 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import { baseStoreIpcChannelPrefixesInjectionToken } from "../base-store/channel-prefix"; +import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../base-store/disable-sync"; +import { persistStateToConfigInjectionToken } from "../base-store/save-to-file"; +import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; +import loggerInjectable from "../logger.injectable"; +import getBasenameOfPathInjectable from "../path/get-basename.injectable"; +import { enlistMessageChannelListenerInjectionToken } from "../utils/channel/enlist-message-channel-listener-injection-token"; +import { sendMessageToChannelInjectionToken } from "../utils/channel/message-to-channel-injection-token"; +import entityPreferencesStoreMigrationVersionInjectable from "./migration-version.injectable"; +import { EntityPreferencesStore } from "./store"; + +const entityPreferencesStoreInjectable = getInjectable({ + id: "entity-preferences-store", + instantiate: (di) => new EntityPreferencesStore({ + directoryForUserData: di.inject(directoryForUserDataInjectable), + getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), + logger: di.inject(loggerInjectable), + storeMigrationVersion: di.inject(entityPreferencesStoreMigrationVersionInjectable), + getBasenameOfPath: di.inject(getBasenameOfPathInjectable), + ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), + persistStateToConfig: di.inject(persistStateToConfigInjectionToken), + enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), + shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), + migrations: {}, + sendMessageToChannel: di.inject(sendMessageToChannelInjectionToken), + }), +}); + +export default entityPreferencesStoreInjectable; diff --git a/src/common/entity-preferences/store.ts b/src/common/entity-preferences/store.ts new file mode 100644 index 0000000000..10f1b69cdd --- /dev/null +++ b/src/common/entity-preferences/store.ts @@ -0,0 +1,49 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { merge } from "lodash"; +import { action, makeObservable, observable } from "mobx"; +import type { PartialDeep } from "type-fest"; +import type { BaseStoreDependencies } from "../base-store/base-store"; +import { BaseStore } from "../base-store/base-store"; + +export interface EntityPreferencesModel { + /** + * Is used for displaying entity icons. + */ + shortName?: string; +} + +export interface EntityPreferencesStoreModel { + entities?: [string, EntityPreferencesModel][]; +} + +export class EntityPreferencesStore extends BaseStore { + @observable readonly preferences = observable.map>(); + + constructor(deps: BaseStoreDependencies) { + super(deps, { + configName: "lens-entity-preferences-store", + }); + + makeObservable(this); + } + + @action + mergePreferences(entityId: string, preferences: PartialDeep): void { + this.preferences.set(entityId, merge(this.preferences.get(entityId), preferences)); + } + + @action + protected fromStore(data: EntityPreferencesStoreModel): void { + this.preferences.replace(data.entities ?? []); + } + + toJSON(): EntityPreferencesStoreModel { + return { + entities: this.preferences.toJSON(), + }; + } +} diff --git a/src/common/hotbars/migration-version.injectable.ts b/src/common/hotbars/migration-version.injectable.ts new file mode 100644 index 0000000000..edf319a2db --- /dev/null +++ b/src/common/hotbars/migration-version.injectable.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; + +const hotbarStoreMigrationVersionInjectable = getInjectable({ + id: "hotbar-store-migration-version", + instantiate: () => "6.4.0", +}); + +export default hotbarStoreMigrationVersionInjectable; diff --git a/src/common/hotbars/store.injectable.ts b/src/common/hotbars/store.injectable.ts index cc15f93bf8..12875af1eb 100644 --- a/src/common/hotbars/store.injectable.ts +++ b/src/common/hotbars/store.injectable.ts @@ -8,7 +8,6 @@ import { HotbarStore } from "./store"; import loggerInjectable from "../logger.injectable"; import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; import storeMigrationsInjectable from "../base-store/migrations.injectable"; import { hotbarStoreMigrationInjectionToken } from "./migrations-token"; import getBasenameOfPathInjectable from "../path/get-basename.injectable"; @@ -16,6 +15,8 @@ import { baseStoreIpcChannelPrefixesInjectionToken } from "../base-store/channel import { persistStateToConfigInjectionToken } from "../base-store/save-to-file"; import { enlistMessageChannelListenerInjectionToken } from "../utils/channel/enlist-message-channel-listener-injection-token"; import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../base-store/disable-sync"; +import hotbarStoreMigrationVersionInjectable from "./migration-version.injectable"; +import { sendMessageToChannelInjectionToken } from "../utils/channel/message-to-channel-injection-token"; const hotbarStoreInjectable = getInjectable({ id: "hotbar-store", @@ -25,13 +26,14 @@ const hotbarStoreInjectable = getInjectable({ logger: di.inject(loggerInjectable), directoryForUserData: di.inject(directoryForUserDataInjectable), getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), + storeMigrationVersion: di.inject(hotbarStoreMigrationVersionInjectable), migrations: di.inject(storeMigrationsInjectable, hotbarStoreMigrationInjectionToken), getBasenameOfPath: di.inject(getBasenameOfPathInjectable), ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), persistStateToConfig: di.inject(persistStateToConfigInjectionToken), enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), + sendMessageToChannel: di.inject(sendMessageToChannelInjectionToken), }), }); diff --git a/src/common/hotbars/store.ts b/src/common/hotbars/store.ts index 709242f20e..18a5da1319 100644 --- a/src/common/hotbars/store.ts +++ b/src/common/hotbars/store.ts @@ -15,6 +15,7 @@ import { hotbarTooManyItemsChannel } from "../ipc/hotbar"; import type { GeneralEntity } from "../catalog-entities"; import type { Logger } from "../logger"; import assert from "assert"; +import { computeDefaultShortName, getShortName } from "../catalog/helpers"; export interface HotbarStoreModel { hotbars: Hotbar[]; @@ -82,18 +83,30 @@ export class HotbarStore extends BaseStore { if (!data.hotbars || !data.hotbars.length) { const hotbar = getEmptyHotbar("Default"); const { - metadata: { uid, name, source }, + metadata: { + uid, + name, + source, + }, } = this.dependencies.catalogCatalogEntity; - const initialItem = { entity: { uid, name, source }}; - - hotbar.items[0] = initialItem; + hotbar.items[0] = { + entity: { + uid, + name, + source, + shortName: getShortName(this.dependencies.catalogCatalogEntity), + }, + }; this.hotbars = [hotbar]; } else { this.hotbars = data.hotbars; } - this.hotbars.forEach(ensureExactHotbarItemLength); + for (const hotbar of this.hotbars) { + ensureExactHotbarItemLength(hotbar); + ensureNamesAndShortNames(hotbar); + } if (data.activeHotbarId) { this._activeHotbarId = data.activeHotbarId; @@ -102,6 +115,12 @@ export class HotbarStore extends BaseStore { if (!this._activeHotbarId) { this._activeHotbarId = this.hotbars[0].id; } + + const activeHotbarExists = this.hotbars.findIndex(hotbar => hotbar.id === this._activeHotbarId) >= 0; + + if (!activeHotbarExists) { + this._activeHotbarId = this.hotbars[0].id; + } } toJSON(): HotbarStoreModel { @@ -168,6 +187,7 @@ export class HotbarStore extends BaseStore { const hotbar = this.getActive(); const uid = item.getId(); const name = item.getName(); + const shortName = getShortName(item); if (typeof uid !== "string") { throw new TypeError("CatalogEntity's ID must be a string"); @@ -177,6 +197,10 @@ export class HotbarStore extends BaseStore { throw new TypeError("CatalogEntity's NAME must be a string"); } + if (typeof shortName !== "string") { + throw new TypeError("CatalogEntity's SHORT_NAME must be a string"); + } + if (this.isAddedToActive(item)) { return; } @@ -185,6 +209,7 @@ export class HotbarStore extends BaseStore { uid, name, source: item.metadata.source, + shortName, }; const newItem = { entity }; @@ -349,3 +374,23 @@ function ensureExactHotbarItemLength(hotbar: Hotbar) { } } } + +/** + * This function ensures that the data coming in has the correct form + * @param hotbar The hotbar to modify + */ +function ensureNamesAndShortNames(hotbar: Hotbar) { + for (let i = 0; i < hotbar.items.length; i += 1) { + const item = hotbar.items[i]; + + if (!item) { + continue; + } + + if (!item.entity.name || typeof item.entity.name !== "string") { + hotbar.items[i] = null; + } else if (!item.entity.shortName || typeof item.entity.shortName !== "string") { + item.entity.shortName = computeDefaultShortName(item.entity.name); + } + } +} diff --git a/src/common/hotbars/types.ts b/src/common/hotbars/types.ts index 6370fe136d..f478ad412f 100644 --- a/src/common/hotbars/types.ts +++ b/src/common/hotbars/types.ts @@ -7,15 +7,16 @@ import * as uuid from "uuid"; import type { Tuple } from "../utils"; import { tuple } from "../utils"; +export interface HotbarItemEntity { + uid: string; + name: string; + shortName: string; + source?: string; +} + export interface HotbarItem { - entity: { - uid: string; - name: string; - source?: string; - }; - params?: { - [key: string]: string; - }; + entity: HotbarItemEntity; + params?: Partial>; } export type Hotbar = Required; diff --git a/src/common/ipc/catalog.ts b/src/common/ipc/catalog.ts deleted file mode 100644 index 3d316d211c..0000000000 --- a/src/common/ipc/catalog.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -/** - * This is used to activate a specific entity in the renderer main frame - */ -export const catalogEntityRunListener = "catalog-entity:run"; - -/** - * This is broadcast on whenever there is an update to any catalog item - */ -export const catalogItemsChannel = "catalog:items"; - -/** - * This can be sent from renderer to main to initialize a broadcast of ITEMS - */ -export const catalogInitChannel = "catalog:init"; diff --git a/src/common/test-utils/use-fake-time.ts b/src/common/test-utils/use-fake-time.ts index e455984861..e0c580df31 100644 --- a/src/common/test-utils/use-fake-time.ts +++ b/src/common/test-utils/use-fake-time.ts @@ -16,10 +16,21 @@ export const advanceFakeTime = (milliseconds: number) => { }); }; -export const testUsingFakeTime = (dateTime = "2015-10-21T07:28:00Z") => { +export interface TestUsingFakeTimeOptions { + dateTime?: string; + autoAdvance?: boolean; +} + +export const testUsingFakeTime = ({ autoAdvance = false, dateTime: dateTime = "2015-10-21T07:28:00Z" }: TestUsingFakeTimeOptions = {}) => { usingFakeTime = true; + const setInterval = global.setInterval; + jest.useFakeTimers(); + if (autoAdvance) { + setInterval(() => advanceFakeTime(100), 100); + } + jest.setSystemTime(new Date(dateTime)); }; diff --git a/src/common/user-store/migration-version.injectable.ts b/src/common/user-store/migration-version.injectable.ts new file mode 100644 index 0000000000..623ffe4c9a --- /dev/null +++ b/src/common/user-store/migration-version.injectable.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; + +const userStoreMigrationVersionInjectable = getInjectable({ + id: "user-store-migration-version", + instantiate: () => "6.4.0", +}); + +export default userStoreMigrationVersionInjectable; diff --git a/src/common/user-store/user-store.injectable.ts b/src/common/user-store/user-store.injectable.ts index 3b45b03b1d..153811c973 100644 --- a/src/common/user-store/user-store.injectable.ts +++ b/src/common/user-store/user-store.injectable.ts @@ -9,7 +9,6 @@ import emitAppEventInjectable from "../app-event-bus/emit-event.injectable"; import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; import loggerInjectable from "../logger.injectable"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; import storeMigrationsInjectable from "../base-store/migrations.injectable"; import { userStoreMigrationInjectionToken } from "./migrations-token"; import { baseStoreIpcChannelPrefixesInjectionToken } from "../base-store/channel-prefix"; @@ -18,6 +17,8 @@ import { persistStateToConfigInjectionToken } from "../base-store/save-to-file"; import getBasenameOfPathInjectable from "../path/get-basename.injectable"; import { enlistMessageChannelListenerInjectionToken } from "../utils/channel/enlist-message-channel-listener-injection-token"; import userStorePreferenceDescriptorsInjectable from "./preference-descriptors.injectable"; +import userStoreMigrationVersionInjectable from "./migration-version.injectable"; +import { sendMessageToChannelInjectionToken } from "../utils/channel/message-to-channel-injection-token"; const userStoreInjectable = getInjectable({ id: "user-store", @@ -28,7 +29,7 @@ const userStoreInjectable = getInjectable({ directoryForUserData: di.inject(directoryForUserDataInjectable), getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), logger: di.inject(loggerInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), + storeMigrationVersion: di.inject(userStoreMigrationVersionInjectable), migrations: di.inject(storeMigrationsInjectable, userStoreMigrationInjectionToken), getBasenameOfPath: di.inject(getBasenameOfPathInjectable), ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), @@ -36,6 +37,7 @@ const userStoreInjectable = getInjectable({ enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), preferenceDescriptors: di.inject(userStorePreferenceDescriptorsInjectable), + sendMessageToChannel: di.inject(sendMessageToChannelInjectionToken), }), }); diff --git a/src/common/utils/channel/enlist-message-channel-listener-injection-token.ts b/src/common/utils/channel/enlist-message-channel-listener-injection-token.ts index 34f62d51d5..bd9f6ca010 100644 --- a/src/common/utils/channel/enlist-message-channel-listener-injection-token.ts +++ b/src/common/utils/channel/enlist-message-channel-listener-injection-token.ts @@ -6,7 +6,7 @@ import { getInjectionToken } from "@ogre-tools/injectable"; import type { Disposer } from "../disposer"; import type { MessageChannel, MessageChannelListener } from "./message-channel-listener-injection-token"; -export type EnlistMessageChannelListener = (listener: MessageChannelListener>) => Disposer; +export type EnlistMessageChannelListener = (listener: MessageChannelListener>) => Disposer; export const enlistMessageChannelListenerInjectionToken = getInjectionToken({ id: "enlist-message-channel-listener", diff --git a/src/common/utils/iter.ts b/src/common/utils/iter.ts index dc1c4621fd..8aa80ebbe0 100644 --- a/src/common/utils/iter.ts +++ b/src/common/utils/iter.ts @@ -14,6 +14,7 @@ interface Iterator extends Iterable { flatMap(fn: (val: T) => U[]): Iterator; concat(src2: IterableIterator): Iterator; join(sep?: string): string; + take(count: number): Iterator; } export function chain(src: IterableIterator): Iterator { @@ -26,6 +27,7 @@ export function chain(src: IterableIterator): Iterator { join: (sep) => join(src, sep), collect: (fn) => fn(src), concat: (src2) => chain(concat(src, src2)), + take: (count) => chain(take(src, count)), [Symbol.iterator]: () => src, }; } diff --git a/src/common/utils/reactive-now/reactive-now.test.tsx b/src/common/utils/reactive-now/reactive-now.test.tsx index ab9b185438..6bb8940ca7 100644 --- a/src/common/utils/reactive-now/reactive-now.test.tsx +++ b/src/common/utils/reactive-now/reactive-now.test.tsx @@ -15,7 +15,9 @@ describe("reactiveNow", () => { let someComputed: IComputedValue; beforeEach(() => { - testUsingFakeTime("2015-10-21T07:28:00Z"); + testUsingFakeTime({ + dateTime: "2015-10-21T07:28:00Z", + }); someComputed = computed(() => { const currentTimestamp = reactiveNow(); diff --git a/src/common/vars/store-migration-version.injectable.ts b/src/common/vars/store-migration-version.injectable.ts deleted file mode 100644 index eb2b7aa8cc..0000000000 --- a/src/common/vars/store-migration-version.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import applicationInformationToken from "./application-information-token"; - -const storeMigrationVersionInjectable = getInjectable({ - id: "store-migration-version", - instantiate: (di) => di.inject(applicationInformationToken).version, -}); - -export default storeMigrationVersionInjectable; diff --git a/src/common/weblinks-store/migration-version.injectable.ts b/src/common/weblinks-store/migration-version.injectable.ts new file mode 100644 index 0000000000..66fba0acde --- /dev/null +++ b/src/common/weblinks-store/migration-version.injectable.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; + +const weblinksStoreMigrationVersionInjectable = getInjectable({ + id: "weblinks-store-migration-version", + instantiate: () => "6.4.0", +}); + +export default weblinksStoreMigrationVersionInjectable; diff --git a/src/common/weblinks-store/weblink-store.injectable.ts b/src/common/weblinks-store/weblink-store.injectable.ts index cf793a2e58..16cdb15e21 100644 --- a/src/common/weblinks-store/weblink-store.injectable.ts +++ b/src/common/weblinks-store/weblink-store.injectable.ts @@ -12,8 +12,9 @@ import getConfigurationFileModelInjectable from "../get-configuration-file-model import loggerInjectable from "../logger.injectable"; import getBasenameOfPathInjectable from "../path/get-basename.injectable"; import { enlistMessageChannelListenerInjectionToken } from "../utils/channel/enlist-message-channel-listener-injection-token"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; +import { sendMessageToChannelInjectionToken } from "../utils/channel/message-to-channel-injection-token"; import { weblinkStoreMigrationInjectionToken } from "./migration-token"; +import weblinksStoreMigrationVersionInjectable from "./migration-version.injectable"; import { WeblinkStore } from "./weblink-store"; const weblinkStoreInjectable = getInjectable({ @@ -22,13 +23,14 @@ const weblinkStoreInjectable = getInjectable({ directoryForUserData: di.inject(directoryForUserDataInjectable), getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), logger: di.inject(loggerInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), + storeMigrationVersion: di.inject(weblinksStoreMigrationVersionInjectable), migrations: di.inject(storeMigrationsInjectable, weblinkStoreMigrationInjectionToken), getBasenameOfPath: di.inject(getBasenameOfPathInjectable), ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), persistStateToConfig: di.inject(persistStateToConfigInjectionToken), enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), + sendMessageToChannel: di.inject(sendMessageToChannelInjectionToken), }), }); diff --git a/src/extensions/extension-loader/extension-is-enabled-for-cluster.injectable.ts b/src/extensions/extension-loader/extension-is-enabled-for-cluster.injectable.ts index 23e1c87561..fae0b3d7cb 100644 --- a/src/extensions/extension-loader/extension-is-enabled-for-cluster.injectable.ts +++ b/src/extensions/extension-loader/extension-is-enabled-for-cluster.injectable.ts @@ -14,16 +14,14 @@ interface ExtensionIsEnabledForCluster { const extensionIsEnabledForClusterInjectable = getInjectable({ id: "extension-is-enabled-for-cluster", - instantiate: async ( - di, - { extension, cluster }: ExtensionIsEnabledForCluster, - ) => (await extension.isEnabledForCluster(cluster)) as boolean, + instantiate: async (di, { extension, cluster }: ExtensionIsEnabledForCluster) => ( + await extension.isEnabledForCluster(cluster) as boolean + ), lifecycle: lifecycleEnum.keyedSingleton({ - getInstanceKey: ( - di, - { extension, cluster }: ExtensionIsEnabledForCluster, - ) => `${extension.sanitizedExtensionId}-${cluster.getId()}`, + getInstanceKey: (di, { extension, cluster }: ExtensionIsEnabledForCluster) => ( + `${extension.sanitizedExtensionId}-${cluster.getId()}` + ), }), }); diff --git a/src/extensions/extension-loader/extension/extension.injectable.ts b/src/extensions/extension-loader/extension/extension.injectable.ts index 6b9424cea4..4b8b03e170 100644 --- a/src/extensions/extension-loader/extension/extension.injectable.ts +++ b/src/extensions/extension-loader/extension/extension.injectable.ts @@ -43,7 +43,7 @@ const extensionInjectable = getInjectable({ reactionDisposer.push( // injectables is either an array or a computed array, in which case - // we need to update the registered injectables with a reaction every time they change + // we need to update the registered injectables with a reaction every time they change reaction( () => Array.isArray(injectables) ? injectables : injectables.get(), (currentInjectables, previousInjectables = []) => { diff --git a/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.injectable.ts b/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.injectable.ts index b511437da9..f0042bfe3a 100644 --- a/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.injectable.ts +++ b/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.injectable.ts @@ -11,12 +11,13 @@ import randomBytesInjectable from "../../../common/utils/random-bytes.injectable import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable"; import loggerInjectable from "../../../common/logger.injectable"; -import storeMigrationVersionInjectable from "../../../common/vars/store-migration-version.injectable"; import { baseStoreIpcChannelPrefixesInjectionToken } from "../../../common/base-store/channel-prefix"; import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../../../common/base-store/disable-sync"; import { persistStateToConfigInjectionToken } from "../../../common/base-store/save-to-file"; import getBasenameOfPathInjectable from "../../../common/path/get-basename.injectable"; import { enlistMessageChannelListenerInjectionToken } from "../../../common/utils/channel/enlist-message-channel-listener-injection-token"; +import fileSystemProvisionerStoreMigrationVersionInjectable from "./migration-version.injectable"; +import { sendMessageToChannelInjectionToken } from "../../../common/utils/channel/message-to-channel-injection-token"; const fileSystemProvisionerStoreInjectable = getInjectable({ id: "file-system-provisioner-store", @@ -29,13 +30,14 @@ const fileSystemProvisionerStoreInjectable = getInjectable({ directoryForUserData: di.inject(directoryForUserDataInjectable), getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), logger: di.inject(loggerInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), + storeMigrationVersion: di.inject(fileSystemProvisionerStoreMigrationVersionInjectable), migrations: {}, getBasenameOfPath: di.inject(getBasenameOfPathInjectable), ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), persistStateToConfig: di.inject(persistStateToConfigInjectionToken), enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), + sendMessageToChannel: di.inject(sendMessageToChannelInjectionToken), }), }); diff --git a/src/extensions/extension-loader/file-system-provisioner-store/migration-version.injectable.ts b/src/extensions/extension-loader/file-system-provisioner-store/migration-version.injectable.ts new file mode 100644 index 0000000000..eb7785fd9e --- /dev/null +++ b/src/extensions/extension-loader/file-system-provisioner-store/migration-version.injectable.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; + +const fileSystemProvisionerStoreMigrationVersionInjectable = getInjectable({ + id: "file-system-provisioner-store-migration-version", + instantiate: () => "6.4.0", +}); + +export default fileSystemProvisionerStoreMigrationVersionInjectable; diff --git a/src/extensions/extension-store.ts b/src/extensions/extension-store.ts index 271175f0a4..2c48d727bd 100644 --- a/src/extensions/extension-store.ts +++ b/src/extensions/extension-store.ts @@ -14,13 +14,13 @@ import { getLegacyGlobalDiForExtensionApi } from "./as-legacy-globals-for-extens import directoryForUserDataInjectable from "../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; import getConfigurationFileModelInjectable from "../common/get-configuration-file-model/get-configuration-file-model.injectable"; import loggerInjectable from "../common/logger.injectable"; -import storeMigrationVersionInjectable from "../common/vars/store-migration-version.injectable"; import type { Migrations } from "conf/dist/source/types"; import { baseStoreIpcChannelPrefixesInjectionToken } from "../common/base-store/channel-prefix"; import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../common/base-store/disable-sync"; import { persistStateToConfigInjectionToken } from "../common/base-store/save-to-file"; import getBasenameOfPathInjectable from "../common/path/get-basename.injectable"; import { enlistMessageChannelListenerInjectionToken } from "../common/utils/channel/enlist-message-channel-listener-injection-token"; +import { sendMessageToChannelInjectionToken } from "../common/utils/channel/message-to-channel-injection-token"; export interface ExtensionStoreParams extends BaseStoreParams { migrations?: Migrations; @@ -52,18 +52,29 @@ export abstract class ExtensionStore extends BaseStore { constructor({ migrations, ...params }: ExtensionStoreParams) { const di = getLegacyGlobalDiForExtensionApi(); - super({ + super(Object.defineProperty({ directoryForUserData: di.inject(directoryForUserDataInjectable), getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), logger: di.inject(loggerInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), + storeMigrationVersion: "", migrations: migrations as Migrations>, getBasenameOfPath: di.inject(getBasenameOfPathInjectable), ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), persistStateToConfig: di.inject(persistStateToConfigInjectionToken), enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), - }, params); + sendMessageToChannel: di.inject(sendMessageToChannelInjectionToken), + }, "storeMigrationVersion", { + get: () => { + const { version } = this.extension ?? {}; + + if (!version) { + throw new Error("Extension must be loaded before creating store"); + } + + return version; + }, + }), params); } /** @@ -78,8 +89,6 @@ export abstract class ExtensionStore extends BaseStore { loadExtension(extension: LensExtension) { this.extension = extension; - this.params.projectVersion ??= this.extension.version; - return super.load(); } diff --git a/src/extensions/extensions-store/extensions-store.injectable.ts b/src/extensions/extensions-store/extensions-store.injectable.ts index 9f5ff83270..ebb2d1ae78 100644 --- a/src/extensions/extensions-store/extensions-store.injectable.ts +++ b/src/extensions/extensions-store/extensions-store.injectable.ts @@ -11,8 +11,9 @@ import getConfigurationFileModelInjectable from "../../common/get-configuration- import loggerInjectable from "../../common/logger.injectable"; import getBasenameOfPathInjectable from "../../common/path/get-basename.injectable"; import { enlistMessageChannelListenerInjectionToken } from "../../common/utils/channel/enlist-message-channel-listener-injection-token"; -import storeMigrationVersionInjectable from "../../common/vars/store-migration-version.injectable"; +import { sendMessageToChannelInjectionToken } from "../../common/utils/channel/message-to-channel-injection-token"; import { ExtensionsStore } from "./extensions-store"; +import extensionsStoreMigrationVersionInjectable from "./migration-version.injectable"; const extensionsStoreInjectable = getInjectable({ id: "extensions-store", @@ -20,13 +21,14 @@ const extensionsStoreInjectable = getInjectable({ directoryForUserData: di.inject(directoryForUserDataInjectable), getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), logger: di.inject(loggerInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), + storeMigrationVersion: di.inject(extensionsStoreMigrationVersionInjectable), migrations: {}, getBasenameOfPath: di.inject(getBasenameOfPathInjectable), ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), persistStateToConfig: di.inject(persistStateToConfigInjectionToken), enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), + sendMessageToChannel: di.inject(sendMessageToChannelInjectionToken), }), }); diff --git a/src/extensions/extensions-store/migration-version.injectable.ts b/src/extensions/extensions-store/migration-version.injectable.ts new file mode 100644 index 0000000000..12ed57a700 --- /dev/null +++ b/src/extensions/extensions-store/migration-version.injectable.ts @@ -0,0 +1,12 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; + +const extensionsStoreMigrationVersionInjectable = getInjectable({ + id: "extensions-store-migration-version", + instantiate: () => "6.4.0", +}); + +export default extensionsStoreMigrationVersionInjectable; diff --git a/src/extensions/lens-extension-set-dependencies.ts b/src/extensions/lens-extension-set-dependencies.ts index 7b7c62597a..89e5a58cfe 100644 --- a/src/extensions/lens-extension-set-dependencies.ts +++ b/src/extensions/lens-extension-set-dependencies.ts @@ -7,7 +7,7 @@ import type { IComputedValue } from "mobx"; import type { CatalogCategoryRegistry } from "../common/catalog"; import type { NavigateToRoute } from "../common/front-end-routing/navigate-to-route-injection-token"; import type { Route } from "../common/front-end-routing/front-end-route-injection-token"; -import type { CatalogEntityRegistry as MainCatalogEntityRegistry } from "../main/catalog"; +import type { CatalogEntityRegistry as MainCatalogEntityRegistry } from "../main/catalog/entity-registry"; import type { CatalogEntityRegistry as RendererCatalogEntityRegistry } from "../renderer/api/catalog/entity/registry"; import type { GetExtensionPageParameters } from "../renderer/routes/get-extension-page-parameters.injectable"; import type { FileSystemProvisionerStore } from "./extension-loader/file-system-provisioner-store/file-system-provisioner-store"; diff --git a/src/features/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap b/src/features/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap index ccf3a4153f..9c479f48cd 100644 --- a/src/features/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap +++ b/src/features/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap @@ -162,22 +162,42 @@ exports[`extension special characters in page registrations renders 1`] = ` class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -366,22 +386,42 @@ exports[`extension special characters in page registrations when navigating to r class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
diff --git a/src/features/__snapshots__/navigate-to-extension-page.test.tsx.snap b/src/features/__snapshots__/navigate-to-extension-page.test.tsx.snap index b66ac6c4b3..63cbe79419 100644 --- a/src/features/__snapshots__/navigate-to-extension-page.test.tsx.snap +++ b/src/features/__snapshots__/navigate-to-extension-page.test.tsx.snap @@ -162,22 +162,42 @@ exports[`navigate to extension page renders 1`] = ` class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -366,22 +386,42 @@ exports[`navigate to extension page when extension navigates to child route rend class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -586,22 +626,42 @@ exports[`navigate to extension page when extension navigates to route with param class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -806,22 +866,42 @@ exports[`navigate to extension page when extension navigates to route without pa class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -1026,22 +1106,42 @@ exports[`navigate to extension page when extension navigates to route without pa class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
diff --git a/src/features/__snapshots__/navigating-between-routes.test.tsx.snap b/src/features/__snapshots__/navigating-between-routes.test.tsx.snap index 98b22c8ec3..03e9585b4e 100644 --- a/src/features/__snapshots__/navigating-between-routes.test.tsx.snap +++ b/src/features/__snapshots__/navigating-between-routes.test.tsx.snap @@ -86,22 +86,42 @@ exports[`navigating between routes given route with optional path parameters whe class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -290,22 +310,42 @@ exports[`navigating between routes given route without path parameters when navi class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
diff --git a/src/features/add-cluster/__snapshots__/navigation-using-application-menu.test.tsx.snap b/src/features/add-cluster/__snapshots__/navigation-using-application-menu.test.tsx.snap index 28cdcc4b8d..b8159b70b9 100644 --- a/src/features/add-cluster/__snapshots__/navigation-using-application-menu.test.tsx.snap +++ b/src/features/add-cluster/__snapshots__/navigation-using-application-menu.test.tsx.snap @@ -162,22 +162,42 @@ exports[`add-cluster - navigation using application menu renders 1`] = ` class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -450,22 +470,42 @@ exports[`add-cluster - navigation using application menu when navigating to add class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
diff --git a/src/features/application-menu/application-menu.test.ts b/src/features/application-menu/application-menu.test.ts index 0db35db744..1d7d3244fe 100644 --- a/src/features/application-menu/application-menu.test.ts +++ b/src/features/application-menu/application-menu.test.ts @@ -5,7 +5,7 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import populateApplicationMenuInjectable from "./main/populate-application-menu.injectable"; -import { advanceFakeTime, testUsingFakeTime } from "../../common/test-utils/use-fake-time"; +import { advanceFakeTime } from "../../common/test-utils/use-fake-time"; import { getCompositePaths } from "../../common/utils/composite/get-composite-paths/get-composite-paths"; import platformInjectable, { allPlatforms } from "../../common/vars/platform.injectable"; @@ -14,8 +14,6 @@ describe.each(allPlatforms)("application-menu, given platform is '%s'", (platfor let populateApplicationMenuMock: jest.Mock; beforeEach(async () => { - testUsingFakeTime(); - populateApplicationMenuMock = jest.fn(); builder = getApplicationBuilder(); diff --git a/src/features/application-menu/handling-of-orphan-application-menu-items.test.ts b/src/features/application-menu/handling-of-orphan-application-menu-items.test.ts index 7f35861a45..6d27370b43 100644 --- a/src/features/application-menu/handling-of-orphan-application-menu-items.test.ts +++ b/src/features/application-menu/handling-of-orphan-application-menu-items.test.ts @@ -5,7 +5,7 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import populateApplicationMenuInjectable from "./main/populate-application-menu.injectable"; -import { advanceFakeTime, testUsingFakeTime } from "../../common/test-utils/use-fake-time"; +import { advanceFakeTime } from "../../common/test-utils/use-fake-time"; import { getCompositePaths } from "../../common/utils/composite/get-composite-paths/get-composite-paths"; import { getInjectable } from "@ogre-tools/injectable"; import applicationMenuItemInjectionToken from "./main/menu-items/application-menu-item-injection-token"; @@ -18,8 +18,6 @@ describe("handling-of-orphan-application-menu-items, given orphan menu item", () let logErrorMock: jest.Mock; beforeEach(async () => { - testUsingFakeTime(); - populateApplicationMenuMock = jest.fn(); logErrorMock = jest.fn(); diff --git a/src/features/application-update/__snapshots__/installing-update.test.ts.snap b/src/features/application-update/__snapshots__/installing-update.test.ts.snap index 6e61ea5e5d..462f4d69f5 100644 --- a/src/features/application-update/__snapshots__/installing-update.test.ts.snap +++ b/src/features/application-update/__snapshots__/installing-update.test.ts.snap @@ -163,22 +163,42 @@ exports[`installing update when started renders 1`] = ` class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -448,22 +468,42 @@ exports[`installing update when started when user checks for updates renders 1`] class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -733,22 +773,42 @@ exports[`installing update when started when user checks for updates when new up class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -1043,22 +1103,42 @@ exports[`installing update when started when user checks for updates when new up class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -1353,22 +1433,42 @@ exports[`installing update when started when user checks for updates when new up class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -1638,22 +1738,42 @@ exports[`installing update when started when user checks for updates when no new class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
diff --git a/src/features/application-update/analytics-for-installing-update.test.ts b/src/features/application-update/analytics-for-installing-update.test.ts index ce77d37229..494f22a285 100644 --- a/src/features/application-update/analytics-for-installing-update.test.ts +++ b/src/features/application-update/analytics-for-installing-update.test.ts @@ -17,7 +17,7 @@ import type { DownloadPlatformUpdate } from "./main/download-update/download-pla import downloadPlatformUpdateInjectable from "./main/download-update/download-platform-update/download-platform-update.injectable"; import quitAndInstallUpdateInjectable from "./main/quit-and-install-update.injectable"; import periodicalCheckForUpdatesInjectable from "./child-features/periodical-checking-of-updates/main/periodical-check-for-updates.injectable"; -import { advanceFakeTime, testUsingFakeTime } from "../../common/test-utils/use-fake-time"; +import { advanceFakeTime } from "../../common/test-utils/use-fake-time"; import emitEventInjectable from "../../common/app-event-bus/emit-event.injectable"; import getBuildVersionInjectable from "../../main/vars/build-version/get-build-version.injectable"; @@ -29,9 +29,11 @@ describe("analytics for installing update", () => { let mainDi: DiContainer; beforeEach(async () => { - testUsingFakeTime("2015-10-21T07:28:00Z"); - - builder = getApplicationBuilder(); + builder = getApplicationBuilder({ + useFakeTime: { + dateTime: "2015-10-21T07:28:00Z", + }, + }); analyticsListenerMock = jest.fn(); diff --git a/src/features/application-update/child-features/application-update-using-top-bar/__snapshots__/installing-update-using-topbar-button.test.tsx.snap b/src/features/application-update/child-features/application-update-using-top-bar/__snapshots__/installing-update-using-topbar-button.test.tsx.snap index a4787d0de3..20d1b6be8e 100644 --- a/src/features/application-update/child-features/application-update-using-top-bar/__snapshots__/installing-update-using-topbar-button.test.tsx.snap +++ b/src/features/application-update/child-features/application-update-using-top-bar/__snapshots__/installing-update-using-topbar-button.test.tsx.snap @@ -188,22 +188,42 @@ exports[`encourage user to update when sufficient time passed since update was d class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -473,22 +493,42 @@ exports[`encourage user to update when sufficient time passed since update was d class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
diff --git a/src/features/application-update/child-features/application-update-using-top-bar/installing-update-using-topbar-button.test.tsx b/src/features/application-update/child-features/application-update-using-top-bar/installing-update-using-topbar-button.test.tsx index adede427d2..2487f66289 100644 --- a/src/features/application-update/child-features/application-update-using-top-bar/installing-update-using-topbar-button.test.tsx +++ b/src/features/application-update/child-features/application-update-using-top-bar/installing-update-using-topbar-button.test.tsx @@ -16,7 +16,7 @@ import type { ApplicationBuilder } from "../../../../renderer/components/test-ut import { getApplicationBuilder } from "../../../../renderer/components/test-utils/get-application-builder"; import processCheckingForUpdatesInjectable from "../../main/process-checking-for-updates.injectable"; import quitAndInstallUpdateInjectable from "../../main/quit-and-install-update.injectable"; -import { advanceFakeTime, testUsingFakeTime } from "../../../../common/test-utils/use-fake-time"; +import { advanceFakeTime } from "../../../../common/test-utils/use-fake-time"; function daysToMilliseconds(days: number) { return Math.round(days * 24 * 60 * 60 * 1000); @@ -29,9 +29,11 @@ describe("encourage user to update when sufficient time passed since update was let quitAndInstallUpdateMock: jest.MockedFunction<() => void>; beforeEach(() => { - testUsingFakeTime("2015-10-21T07:28:00Z"); - - applicationBuilder = getApplicationBuilder(); + applicationBuilder = getApplicationBuilder({ + useFakeTime: { + dateTime: "2015-10-21T07:28:00Z", + }, + }); applicationBuilder.beforeApplicationStart((mainDi) => { checkForPlatformUpdatesMock = asyncFn(); diff --git a/src/features/application-update/child-features/application-update-using-tray/__snapshots__/installing-update-using-tray.test.ts.snap b/src/features/application-update/child-features/application-update-using-tray/__snapshots__/installing-update-using-tray.test.ts.snap index a2ea7a417b..c343d4dd5a 100644 --- a/src/features/application-update/child-features/application-update-using-tray/__snapshots__/installing-update-using-tray.test.ts.snap +++ b/src/features/application-update/child-features/application-update-using-tray/__snapshots__/installing-update-using-tray.test.ts.snap @@ -163,22 +163,42 @@ exports[`installing update using tray when started renders 1`] = ` class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -448,22 +468,42 @@ exports[`installing update using tray when started when user checks for updates class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -733,22 +773,42 @@ exports[`installing update using tray when started when user checks for updates class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -1043,22 +1103,42 @@ exports[`installing update using tray when started when user checks for updates class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -1353,22 +1433,42 @@ exports[`installing update using tray when started when user checks for updates class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -1638,22 +1738,42 @@ exports[`installing update using tray when started when user checks for updates class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
diff --git a/src/features/application-update/child-features/force-user-to-update-when-too-long-time-since-update-was-downloaded/__snapshots__/force-user-to-update-when-too-long-time-since-update-was-downloaded.test.ts.snap b/src/features/application-update/child-features/force-user-to-update-when-too-long-time-since-update-was-downloaded/__snapshots__/force-user-to-update-when-too-long-time-since-update-was-downloaded.test.ts.snap index 95101e8294..2628b888b9 100644 --- a/src/features/application-update/child-features/force-user-to-update-when-too-long-time-since-update-was-downloaded/__snapshots__/force-user-to-update-when-too-long-time-since-update-was-downloaded.test.ts.snap +++ b/src/features/application-update/child-features/force-user-to-update-when-too-long-time-since-update-was-downloaded/__snapshots__/force-user-to-update-when-too-long-time-since-update-was-downloaded.test.ts.snap @@ -188,22 +188,42 @@ exports[`force user to update when too long since update was downloaded when app class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -498,22 +518,42 @@ exports[`force user to update when too long since update was downloaded when app class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -855,22 +895,42 @@ exports[`force user to update when too long since update was downloaded when app class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
diff --git a/src/features/application-update/child-features/force-user-to-update-when-too-long-time-since-update-was-downloaded/force-user-to-update-when-too-long-time-since-update-was-downloaded.test.ts b/src/features/application-update/child-features/force-user-to-update-when-too-long-time-since-update-was-downloaded/force-user-to-update-when-too-long-time-since-update-was-downloaded.test.ts index 39ac24570a..813813a21e 100644 --- a/src/features/application-update/child-features/force-user-to-update-when-too-long-time-since-update-was-downloaded/force-user-to-update-when-too-long-time-since-update-was-downloaded.test.ts +++ b/src/features/application-update/child-features/force-user-to-update-when-too-long-time-since-update-was-downloaded/force-user-to-update-when-too-long-time-since-update-was-downloaded.test.ts @@ -14,7 +14,7 @@ import type { DiContainer } from "@ogre-tools/injectable"; import processCheckingForUpdatesInjectable from "../../main/process-checking-for-updates.injectable"; import type { RenderResult } from "@testing-library/react"; import { fireEvent } from "@testing-library/react"; -import { advanceFakeTime, testUsingFakeTime } from "../../../../common/test-utils/use-fake-time"; +import { advanceFakeTime } from "../../../../common/test-utils/use-fake-time"; import quitAndInstallUpdateInjectable from "../../main/quit-and-install-update.injectable"; import timeAfterUpdateMustBeInstalledInjectable from "./renderer/force-update-modal/time-after-update-must-be-installed.injectable"; import secondsAfterInstallStartsInjectable from "./renderer/force-update-modal/seconds-after-install-starts.injectable"; @@ -31,9 +31,11 @@ describe("force user to update when too long since update was downloaded", () => let quitAndInstallUpdateMock: jest.Mock; beforeEach(() => { - testUsingFakeTime("2015-10-21T07:28:00Z"); - - applicationBuilder = getApplicationBuilder(); + applicationBuilder = getApplicationBuilder({ + useFakeTime: { + dateTime: "2015-10-21T07:28:00Z", + }, + }); applicationBuilder.beforeApplicationStart(mainDi => { checkForPlatformUpdatesMock = asyncFn(); diff --git a/src/features/application-update/child-features/periodical-checking-of-updates/__snapshots__/periodical-checking-of-updates.test.ts.snap b/src/features/application-update/child-features/periodical-checking-of-updates/__snapshots__/periodical-checking-of-updates.test.ts.snap index 413477ef9a..f938ad9e49 100644 --- a/src/features/application-update/child-features/periodical-checking-of-updates/__snapshots__/periodical-checking-of-updates.test.ts.snap +++ b/src/features/application-update/child-features/periodical-checking-of-updates/__snapshots__/periodical-checking-of-updates.test.ts.snap @@ -163,22 +163,42 @@ exports[`periodical checking of updates given updater is enabled and configurati class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
diff --git a/src/features/application-update/child-features/periodical-checking-of-updates/periodical-checking-of-updates.test.ts b/src/features/application-update/child-features/periodical-checking-of-updates/periodical-checking-of-updates.test.ts index 22765b85de..546e19848b 100644 --- a/src/features/application-update/child-features/periodical-checking-of-updates/periodical-checking-of-updates.test.ts +++ b/src/features/application-update/child-features/periodical-checking-of-updates/periodical-checking-of-updates.test.ts @@ -9,7 +9,7 @@ import electronUpdaterIsActiveInjectable from "../../../../main/electron-app/fea import publishIsConfiguredInjectable from "../../main/updating-is-enabled/publish-is-configured/publish-is-configured.injectable"; import processCheckingForUpdatesInjectable from "../../main/process-checking-for-updates.injectable"; import periodicalCheckForUpdatesInjectable from "./main/periodical-check-for-updates.injectable"; -import { advanceFakeTime, testUsingFakeTime } from "../../../../common/test-utils/use-fake-time"; +import { advanceFakeTime } from "../../../../common/test-utils/use-fake-time"; const ENOUGH_TIME = 1000 * 60 * 60 * 2; @@ -18,9 +18,11 @@ describe("periodical checking of updates", () => { let processCheckingForUpdatesMock: jest.Mock; beforeEach(() => { - testUsingFakeTime("2015-10-21T07:28:00Z"); - - builder = getApplicationBuilder(); + builder = getApplicationBuilder({ + useFakeTime: { + dateTime: "2015-10-21T07:28:00Z", + }, + }); builder.beforeApplicationStart((mainDi) => { mainDi.unoverride(periodicalCheckForUpdatesInjectable); diff --git a/src/features/application-update/child-features/selection-of-update-stability/__snapshots__/selection-of-update-stability.test.ts.snap b/src/features/application-update/child-features/selection-of-update-stability/__snapshots__/selection-of-update-stability.test.ts.snap index 7da7f42004..b779a2e1bf 100644 --- a/src/features/application-update/child-features/selection-of-update-stability/__snapshots__/selection-of-update-stability.test.ts.snap +++ b/src/features/application-update/child-features/selection-of-update-stability/__snapshots__/selection-of-update-stability.test.ts.snap @@ -163,22 +163,42 @@ exports[`selection of update stability when started renders 1`] = ` class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
diff --git a/src/features/application-update/installing-update.test.ts b/src/features/application-update/installing-update.test.ts index 81529a851f..eee794e9d7 100644 --- a/src/features/application-update/installing-update.test.ts +++ b/src/features/application-update/installing-update.test.ts @@ -16,7 +16,6 @@ import type { DownloadPlatformUpdate } from "./main/download-update/download-pla import downloadPlatformUpdateInjectable from "./main/download-update/download-platform-update/download-platform-update.injectable"; import setUpdateOnQuitInjectable from "../../main/electron-app/features/set-update-on-quit.injectable"; import processCheckingForUpdatesInjectable from "./main/process-checking-for-updates.injectable"; -import { testUsingFakeTime } from "../../common/test-utils/use-fake-time"; import staticFilesDirectoryInjectable from "../../common/vars/static-files-directory.injectable"; describe("installing update", () => { @@ -27,9 +26,11 @@ describe("installing update", () => { let setUpdateOnQuitMock: jest.Mock; beforeEach(() => { - testUsingFakeTime("2015-10-21T07:28:00Z"); - - builder = getApplicationBuilder(); + builder = getApplicationBuilder({ + useFakeTime: { + dateTime: "2015-10-21T07:28:00Z", + }, + }); builder.beforeApplicationStart((mainDi) => { quitAndInstallUpdateMock = jest.fn(); diff --git a/src/features/catalog/__snapshots__/opening-entity-details.test.tsx.snap b/src/features/catalog/__snapshots__/opening-entity-details.test.tsx.snap index 49d085fc85..6a2c87922c 100644 --- a/src/features/catalog/__snapshots__/opening-entity-details.test.tsx.snap +++ b/src/features/catalog/__snapshots__/opening-entity-details.test.tsx.snap @@ -163,22 +163,42 @@ exports[`opening catalog entity details panel renders 1`] = ` class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -579,7 +599,7 @@ exports[`opening catalog entity details panel when navigated to the catalog rend
- 3 items + 7 items
+ ?? +
+ + + + +
+ Add to Hotbar +
+
+
+ KubernetesCluster +
+
+ local +
+
+
+ + connected + +
+ +
+
+
+
+
+
skc
@@ -815,7 +906,7 @@ exports[`opening catalog entity details panel when navigated to the catalog rend
sw
@@ -888,7 +979,7 @@ exports[`opening catalog entity details panel when navigated to the catalog rend
slk
@@ -960,6 +1051,252 @@ exports[`opening catalog entity details panel when navigated to the catalog rend
+
+
+
+
+ + + view_list + + +
+ + Catalog + + + + +
+ Remove from Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
+
+
+
+
+ + + meeting_room + + +
+ + Welcome Page + + + + +
+ Add to Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
+
+
+
+
+ + + settings + + +
+ + Preferences + + + + +
+ Add to Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
@@ -984,22 +1321,42 @@ exports[`opening catalog entity details panel when navigated to the catalog rend class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -1400,7 +1757,7 @@ exports[`opening catalog entity details panel when navigated to the catalog when
- 3 items + 7 items
+ ?? +
+ + + + +
+ Add to Hotbar +
+
+
+ KubernetesCluster +
+
+ local +
+
+
+ + connected + +
+ +
+
+
+
+
+
skc
@@ -1636,7 +2064,7 @@ exports[`opening catalog entity details panel when navigated to the catalog when
sw
@@ -1709,7 +2137,7 @@ exports[`opening catalog entity details panel when navigated to the catalog when
slk
@@ -1781,6 +2209,252 @@ exports[`opening catalog entity details panel when navigated to the catalog when
+
+
+
+
+ + + view_list + + +
+ + Catalog + + + + +
+ Remove from Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
+
+
+
+
+ + + meeting_room + + +
+ + Welcome Page + + + + +
+ Add to Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
+
+
+
+
+ + + settings + + +
+ + Preferences + + + + +
+ Add to Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
@@ -1805,22 +2479,42 @@ exports[`opening catalog entity details panel when navigated to the catalog when class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -2253,7 +2947,7 @@ exports[`opening catalog entity details panel when navigated to the catalog when
- 3 items + 7 items
+ ?? +
+ + + + +
+ Add to Hotbar +
+
+
+ KubernetesCluster +
+
+ local +
+
+
+ + connected + +
+ +
+
+
+
+
+
skc
@@ -2489,7 +3254,7 @@ exports[`opening catalog entity details panel when navigated to the catalog when
sw
@@ -2562,7 +3327,7 @@ exports[`opening catalog entity details panel when navigated to the catalog when
slk
@@ -2634,6 +3399,252 @@ exports[`opening catalog entity details panel when navigated to the catalog when
+
+
+
+
+ + + view_list + + +
+ + Catalog + + + + +
+ Remove from Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
+
+
+
+
+ + + meeting_room + + +
+ + Welcome Page + + + + +
+ Add to Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
+
+
+
+
+ + + settings + + +
+ + Preferences + + + + +
+ Add to Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
@@ -2658,22 +3669,42 @@ exports[`opening catalog entity details panel when navigated to the catalog when class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -3106,7 +4137,7 @@ exports[`opening catalog entity details panel when navigated to the catalog when
- 3 items + 7 items
+ ?? +
+ + + + +
+ Add to Hotbar +
+
+
+ KubernetesCluster +
+
+ local +
+
+
+ + connected + +
+ +
+
+
+
+
+
skc
@@ -3342,7 +4444,7 @@ exports[`opening catalog entity details panel when navigated to the catalog when
sw
@@ -3415,7 +4517,7 @@ exports[`opening catalog entity details panel when navigated to the catalog when
slk
@@ -3487,6 +4589,252 @@ exports[`opening catalog entity details panel when navigated to the catalog when
+
+
+
+
+ + + view_list + + +
+ + Catalog + + + + +
+ Remove from Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
+
+
+
+
+ + + meeting_room + + +
+ + Welcome Page + + + + +
+ Add to Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
+
+
+
+
+ + + settings + + +
+ + Preferences + + + + +
+ Add to Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
@@ -3511,22 +4859,42 @@ exports[`opening catalog entity details panel when navigated to the catalog when class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -3630,38 +4998,6 @@ exports[`opening catalog entity details panel when navigated to the catalog when class="Notifications flex column align-flex-end" />
-
skc
@@ -4210,7 +5546,7 @@ exports[`opening catalog entity details panel when navigated to the catalog when
- 3 items + 7 items
+ ?? +
+ + + + +
+ Add to Hotbar +
+
+
+ KubernetesCluster +
+
+ local +
+
+
+ + connected + +
+ +
+
+
+
+
+
skc
@@ -4446,7 +5853,7 @@ exports[`opening catalog entity details panel when navigated to the catalog when
sw
@@ -4519,7 +5926,7 @@ exports[`opening catalog entity details panel when navigated to the catalog when
slk
@@ -4591,6 +5998,252 @@ exports[`opening catalog entity details panel when navigated to the catalog when
+
+
+
+
+ + + view_list + + +
+ + Catalog + + + + +
+ Remove from Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
+
+
+
+
+ + + meeting_room + + +
+ + Welcome Page + + + + +
+ Add to Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
+
+
+
+
+ + + settings + + +
+ + Preferences + + + + +
+ Add to Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
@@ -4615,22 +6268,42 @@ exports[`opening catalog entity details panel when navigated to the catalog when class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -5051,7 +6724,7 @@ exports[`opening catalog entity details panel when navigated to the catalog when
- 3 items + 7 items
+ ?? +
+ + + + +
+ Add to Hotbar +
+
+
+ KubernetesCluster +
+
+ local +
+
+
+ + connected + +
+ +
+
+
+
+
+
skc
@@ -5287,7 +7031,7 @@ exports[`opening catalog entity details panel when navigated to the catalog when
sw
@@ -5360,7 +7104,7 @@ exports[`opening catalog entity details panel when navigated to the catalog when
slk
@@ -5432,6 +7176,252 @@ exports[`opening catalog entity details panel when navigated to the catalog when
+
+
+
+
+ + + view_list + + +
+ + Catalog + + + + +
+ Remove from Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
+
+
+
+
+ + + meeting_room + + +
+ + Welcome Page + + + + +
+ Add to Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
+
+
+
+
+ + + settings + + +
+ + Preferences + + + + +
+ Add to Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
@@ -5456,22 +7446,42 @@ exports[`opening catalog entity details panel when navigated to the catalog when class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -5892,7 +7902,7 @@ exports[`opening catalog entity details panel when navigated to the catalog when
- 3 items + 7 items
+ ?? +
+ + + + +
+ Add to Hotbar +
+
+
+ KubernetesCluster +
+
+ local +
+
+
+ + connected + +
+ +
+
+
+
+
+
skc
@@ -6128,7 +8209,7 @@ exports[`opening catalog entity details panel when navigated to the catalog when
sw
@@ -6201,7 +8282,7 @@ exports[`opening catalog entity details panel when navigated to the catalog when
slk
@@ -6273,6 +8354,252 @@ exports[`opening catalog entity details panel when navigated to the catalog when
+
+
+
+
+ + + view_list + + +
+ + Catalog + + + + +
+ Remove from Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
+
+
+
+
+ + + meeting_room + + +
+ + Welcome Page + + + + +
+ Add to Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
+
+
+
+
+ + + settings + + +
+ + Preferences + + + + +
+ Add to Hotbar +
+
+
+ General +
+
+ app +
+
+
+ + active + +
+ +
+
@@ -6297,22 +8624,42 @@ exports[`opening catalog entity details panel when navigated to the catalog when class="HotbarItems flex column gaps" >
- Ca + + + view_list + +
+ + + settings + +
@@ -6416,26 +8763,6 @@ exports[`opening catalog entity details panel when navigated to the catalog when class="Notifications flex column align-flex-end" />
-
sw
diff --git a/src/features/catalog/opening-entity-details.test.tsx b/src/features/catalog/opening-entity-details.test.tsx index 8ae49ff548..bc9467f1a6 100644 --- a/src/features/catalog/opening-entity-details.test.tsx +++ b/src/features/catalog/opening-entity-details.test.tsx @@ -5,12 +5,10 @@ import type { DiContainer } from "@ogre-tools/injectable"; import type { RenderResult } from "@testing-library/react"; +import { computed } from "mobx"; import { KubernetesCluster, WebLink } from "../../common/catalog-entities"; -import getClusterByIdInjectable from "../../common/cluster-store/get-by-id.injectable"; -import type { Cluster } from "../../common/cluster/cluster"; import navigateToCatalogInjectable from "../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable"; -import catalogEntityRegistryInjectable from "../../renderer/api/catalog/entity/registry.injectable"; -import createClusterInjectable from "../../renderer/cluster/create-cluster.injectable"; +import catalogEntityRegistryInjectable from "../../main/catalog/entity-registry.injectable"; import { type ApplicationBuilder, getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; describe("opening catalog entity details panel", () => { @@ -20,14 +18,11 @@ describe("opening catalog entity details panel", () => { let clusterEntity: KubernetesCluster; let localClusterEntity: KubernetesCluster; let otherEntity: WebLink; - let cluster: Cluster; beforeEach(async () => { builder = getApplicationBuilder(); - builder.afterWindowStart((windowDi) => { - const createCluster = windowDi.inject(createClusterInjectable); - + builder.beforeApplicationStart((mainDi) => { clusterEntity = new KubernetesCluster({ metadata: { labels: {}, @@ -70,27 +65,8 @@ describe("opening catalog entity details panel", () => { phase: "available", }, }); - cluster = createCluster({ - contextName: clusterEntity.spec.kubeconfigContext, - id: clusterEntity.getId(), - kubeConfigPath: clusterEntity.spec.kubeconfigPath, - }, { - clusterServerUrl: "https://localhost:9999", - }); - // TODO: remove once ClusterStore can be used without overriding it - windowDi.override(getClusterByIdInjectable, () => (clusterId) => { - if (clusterId === cluster.id) { - return cluster; - } - - return undefined; - }); - - // TODO: replace with proper entity source once syncing entities between main and windows is injectable - const catalogEntityRegistry = windowDi.inject(catalogEntityRegistryInjectable); - - catalogEntityRegistry.updateItems([clusterEntity, otherEntity, localClusterEntity]); + mainDi.inject(catalogEntityRegistryInjectable).addComputedSource("test-id", computed(() => [clusterEntity, otherEntity, localClusterEntity])); }); rendered = await builder.render(); diff --git a/src/features/catalog/run/common/channel.ts b/src/features/catalog/run/common/channel.ts new file mode 100644 index 0000000000..6e1273ed9a --- /dev/null +++ b/src/features/catalog/run/common/channel.ts @@ -0,0 +1,10 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { MessageChannel } from "../../../../common/utils/channel/message-channel-listener-injection-token"; + +export const catalogEntityRunChannel: MessageChannel = { + id: "catalog-entity-run", +}; diff --git a/src/features/catalog/run/renderer/emit.injectable.ts b/src/features/catalog/run/renderer/emit.injectable.ts new file mode 100644 index 0000000000..d5140855ec --- /dev/null +++ b/src/features/catalog/run/renderer/emit.injectable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import type { MessageChannelHandler } from "../../../../common/utils/channel/message-channel-listener-injection-token"; +import { sendMessageToChannelInjectionToken } from "../../../../common/utils/channel/message-to-channel-injection-token"; +import { catalogEntityRunChannel } from "../common/channel"; + +export type EmitCatalogEntityRun = MessageChannelHandler; + +const emitCatalogEntityRunInjectable = getInjectable({ + id: "emit-catalog-entity-run", + instantiate: (di): EmitCatalogEntityRun => { + const sendMessageToChannel = di.inject(sendMessageToChannelInjectionToken); + + return (entityId) => sendMessageToChannel(catalogEntityRunChannel, entityId); + }, +}); + +export default emitCatalogEntityRunInjectable; diff --git a/src/features/catalog/run/renderer/listener.injectable.ts b/src/features/catalog/run/renderer/listener.injectable.ts new file mode 100644 index 0000000000..b7eec49665 --- /dev/null +++ b/src/features/catalog/run/renderer/listener.injectable.ts @@ -0,0 +1,33 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { noop } from "../../../../common/utils"; +import { getMessageChannelListenerInjectable } from "../../../../common/utils/channel/message-channel-listener-injection-token"; +import catalogEntityRegistryInjectable from "../../../../renderer/api/catalog/entity/registry.injectable"; +import currentlyInClusterFrameInjectable from "../../../../renderer/routes/currently-in-cluster-frame.injectable"; +import { catalogEntityRunChannel } from "../common/channel"; + +const catalogEntityRunListener = getMessageChannelListenerInjectable({ + channel: catalogEntityRunChannel, + id: "main", + handler: (di) => { + const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); + const currentlyInClusterFrame = di.inject(currentlyInClusterFrameInjectable); + + if (currentlyInClusterFrame) { + return noop; + } + + return (entityId) => { + const entity = catalogEntityRegistry.getById(entityId); + + if (entity) { + catalogEntityRegistry.onRun(entity); + } + }; + }, +}); + +export default catalogEntityRunListener; diff --git a/src/features/catalog/sync/common/sync-channels.ts b/src/features/catalog/sync/common/sync-channels.ts new file mode 100644 index 0000000000..59621589ad --- /dev/null +++ b/src/features/catalog/sync/common/sync-channels.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { MessageChannel } from "../../../../common/utils/channel/message-channel-listener-injection-token"; +import type { CatalogEntityData, CatalogEntityKindData } from "../../../../common/catalog/catalog-entity"; +import type { RequestChannel } from "../../../../common/utils/channel/request-channel-listener-injection-token"; + +export type RawCatalogEntityData = CatalogEntityData & CatalogEntityKindData; + +export const catalogEntityUpdatesChannel: MessageChannel = { + id: "catalog-entity-updates", +}; + +export const catalogInitialEntitiesChannel: RequestChannel = { + id: "catalog-initial-entities", +}; diff --git a/src/features/catalog/sync/main/entity-change-set.injectable.ts b/src/features/catalog/sync/main/entity-change-set.injectable.ts new file mode 100644 index 0000000000..2d435296fb --- /dev/null +++ b/src/features/catalog/sync/main/entity-change-set.injectable.ts @@ -0,0 +1,32 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import type { IComputedValue } from "mobx"; +import { computed } from "mobx"; +import type { CatalogEntityData, CatalogEntityKindData } from "../../../../common/catalog"; +import getEntityShortnameInjectable from "../../../../common/entity-preferences/get-shortname.injectable"; +import { toJS } from "../../../../common/utils"; +import catalogEntityRegistryInjectable from "../../../../main/catalog/entity-registry.injectable"; + +const catalogEntityChangeSetInjectable = getInjectable({ + id: "catalog-entity-change-set", + instantiate: (di): IComputedValue<(CatalogEntityData & CatalogEntityKindData)[]> => { + const getEntityShortname = di.inject(getEntityShortnameInjectable); + const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); + + return computed(() => toJS(catalogEntityRegistry.items.map(({ metadata, spec, status, kind, apiVersion }) => ({ + metadata: { + ...metadata, + shortName: getEntityShortname(metadata.uid) || metadata.shortName, + }, + spec, + status, + kind, + apiVersion, + })))); + }, +}); + +export default catalogEntityChangeSetInjectable; diff --git a/src/features/catalog/sync/main/entity-update-broadcaster.injectable.ts b/src/features/catalog/sync/main/entity-update-broadcaster.injectable.ts new file mode 100644 index 0000000000..6ed2644b47 --- /dev/null +++ b/src/features/catalog/sync/main/entity-update-broadcaster.injectable.ts @@ -0,0 +1,27 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { debounce } from "lodash"; +import type { CatalogEntityData, CatalogEntityKindData } from "../../../../common/catalog"; +import { sendMessageToChannelInjectionToken } from "../../../../common/utils/channel/message-to-channel-injection-token"; +import { catalogEntityUpdatesChannel } from "../common/sync-channels"; + +const entityUpdateBroadcasterInjectable = getInjectable({ + id: "entity-update-broadcaster", + instantiate: (di) => { + const sendMessageToChannel = di.inject(sendMessageToChannelInjectionToken); + + return debounce( + (items: (CatalogEntityData & CatalogEntityKindData)[]) => sendMessageToChannel(catalogEntityUpdatesChannel, items), + 100, + { + leading: true, + trailing: true, + }, + ); + }, +}); + +export default entityUpdateBroadcasterInjectable; diff --git a/src/features/catalog/sync/main/send-listener.injectable.ts b/src/features/catalog/sync/main/send-listener.injectable.ts new file mode 100644 index 0000000000..820b54a401 --- /dev/null +++ b/src/features/catalog/sync/main/send-listener.injectable.ts @@ -0,0 +1,19 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getRequestChannelListenerInjectable } from "../../../../main/utils/channel/channel-listeners/listener-tokens"; +import { catalogInitialEntitiesChannel } from "../common/sync-channels"; +import catalogEntityChangeSetInjectable from "./entity-change-set.injectable"; + +const catalogSendEntityUpdatesListenerInjectable = getRequestChannelListenerInjectable({ + channel: catalogInitialEntitiesChannel, + handler: (di) => { + const catalogEntityChangeSet = di.inject(catalogEntityChangeSetInjectable); + + return () => catalogEntityChangeSet.get(); + }, +}); + +export default catalogSendEntityUpdatesListenerInjectable; diff --git a/src/features/catalog/sync/renderer/entity-updates-listener.injectable.ts b/src/features/catalog/sync/renderer/entity-updates-listener.injectable.ts new file mode 100644 index 0000000000..a286346598 --- /dev/null +++ b/src/features/catalog/sync/renderer/entity-updates-listener.injectable.ts @@ -0,0 +1,22 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getMessageChannelListenerInjectable } from "../../../../common/utils/channel/message-channel-listener-injection-token"; +import catalogEntityRegistryInjectable from "../../../../renderer/api/catalog/entity/registry.injectable"; +import { catalogEntityUpdatesChannel } from "../common/sync-channels"; + +const catalogEntityUpdatesListener = getMessageChannelListenerInjectable({ + channel: catalogEntityUpdatesChannel, + id: "main", + handler: (di) => { + const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); + + return (items) => { + catalogEntityRegistry.updateItems(items); + }; + }, +}); + +export default catalogEntityUpdatesListener; diff --git a/src/features/catalog/sync/renderer/request-entity-updates.injectable.ts b/src/features/catalog/sync/renderer/request-entity-updates.injectable.ts new file mode 100644 index 0000000000..dc72b2bf1e --- /dev/null +++ b/src/features/catalog/sync/renderer/request-entity-updates.injectable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import type { ChannelRequester } from "../../../../main/utils/channel/channel-listeners/listener-tokens"; +import requestFromChannelInjectable from "../../../../renderer/utils/channel/request-from-channel.injectable"; +import { catalogInitialEntitiesChannel } from "../common/sync-channels"; + +export type RequestInitialCatalogEntities = ChannelRequester; + +const requestInitialCatalogEntitiesInjectable = getInjectable({ + id: "request-initial-catalog-entities", + instantiate: (di) => { + const requestFromChannel = di.inject(requestFromChannelInjectable); + + return () => requestFromChannel(catalogInitialEntitiesChannel); + }, +}); + +export default requestInitialCatalogEntitiesInjectable; diff --git a/src/features/catalog/sync/renderer/setup-entity-updates.injectable.ts b/src/features/catalog/sync/renderer/setup-entity-updates.injectable.ts new file mode 100644 index 0000000000..1e73b757ce --- /dev/null +++ b/src/features/catalog/sync/renderer/setup-entity-updates.injectable.ts @@ -0,0 +1,26 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import catalogEntityRegistryInjectable from "../../../../renderer/api/catalog/entity/registry.injectable"; +import { beforeFrameStartsSecondInjectionToken } from "../../../../renderer/before-frame-starts/tokens"; +import requestInitialCatalogEntitiesInjectable from "./request-entity-updates.injectable"; + +const setupCatalogEntityUpdatesInjectable = getInjectable({ + id: "setup-catalog-entity-updates", + instantiate: (di) => ({ + id: "setup-catalog-entity-updates", + run: async () => { + const requestInitialCatalogEntities = di.inject(requestInitialCatalogEntitiesInjectable); + const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); + + const rawEntities = await requestInitialCatalogEntities(); + + catalogEntityRegistry.updateItems(rawEntities); + }, + }), + injectionToken: beforeFrameStartsSecondInjectionToken, +}); + +export default setupCatalogEntityUpdatesInjectable; diff --git a/src/features/cluster/__snapshots__/legacy-extension-adding-cluster-frame-components.test.tsx.snap b/src/features/cluster/__snapshots__/legacy-extension-adding-cluster-frame-components.test.tsx.snap index 6ae0d91f41..9fb179006d 100644 --- a/src/features/cluster/__snapshots__/legacy-extension-adding-cluster-frame-components.test.tsx.snap +++ b/src/features/cluster/__snapshots__/legacy-extension-adding-cluster-frame-components.test.tsx.snap @@ -2,44 +2,6 @@ exports[`legacy extension adding cluster frame components given custom components for cluster view available renders 1`] = `
-
-
-
-
- -
- - - close - - -
- Close -
-
-
-
-
-
@@ -56,16 +18,31 @@ exports[`legacy extension adding cluster frame components given custom component >