diff --git a/src/common/__tests__/user-store.test.ts b/src/common/__tests__/user-store.test.ts index e6fa7b4189..a90e03c75f 100644 --- a/src/common/__tests__/user-store.test.ts +++ b/src/common/__tests__/user-store.test.ts @@ -33,6 +33,8 @@ import writeFileInjectable from "../fs/write-file.injectable"; import { getDiForUnitTesting } from "../../main/getDiForUnitTesting"; import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; +import releaseChannelInjectable from "../vars/release-channel.injectable"; +import defaultUpdateChannelInjectable from "../application-update/selected-update-channel/default-update-channel.injectable"; console = new Console(stdout, stderr); @@ -40,7 +42,7 @@ describe("user store tests", () => { let userStore: UserStore; let di: DiContainer; - beforeEach(() => { + beforeEach(async () => { di = getDiForUnitTesting({ doGeneralOverrides: true }); mockFs(); @@ -50,6 +52,12 @@ describe("user store tests", () => { di.permitSideEffects(getConfigurationFileModelInjectable); di.permitSideEffects(userStoreInjectable); + di.override(releaseChannelInjectable, () => ({ + get: () => "latest" as const, + init: async () => {}, + })); + await di.inject(defaultUpdateChannelInjectable).init(); + di.unoverride(userStoreInjectable); }); @@ -62,6 +70,7 @@ describe("user store tests", () => { mockFs({ "some-directory-for-user-data": { "config.json": "{}", "kube_config": "{}" }}); userStore = di.inject(userStoreInjectable); + userStore.load(); }); it("allows setting and retrieving lastSeenAppVersion", () => { @@ -119,6 +128,7 @@ describe("user store tests", () => { di.override(storeMigrationVersionInjectable, () => "10.0.0"); userStore = di.inject(userStoreInjectable); + userStore.load(); }); it("sets last seen app version to 0.0.0", () => { diff --git a/src/common/application-update/selected-update-channel/default-update-channel.injectable.ts b/src/common/application-update/selected-update-channel/default-update-channel.injectable.ts index ff4fe32e4e..9c00efa204 100644 --- a/src/common/application-update/selected-update-channel/default-update-channel.injectable.ts +++ b/src/common/application-update/selected-update-channel/default-update-channel.injectable.ts @@ -8,7 +8,7 @@ import { updateChannels } from "../update-channels"; const defaultUpdateChannelInjectable = createInitializableState({ id: "default-update-channel", - init: (di) => updateChannels[di.inject(releaseChannelInjectable)], + init: (di) => updateChannels[di.inject(releaseChannelInjectable).get()], }); export default defaultUpdateChannelInjectable; diff --git a/src/common/error-reporting/initialize-sentry-reporting.injectable.ts b/src/common/error-reporting/initialize-sentry-reporting.injectable.ts index d34005f374..778f959739 100644 --- a/src/common/error-reporting/initialize-sentry-reporting.injectable.ts +++ b/src/common/error-reporting/initialize-sentry-reporting.injectable.ts @@ -20,6 +20,7 @@ const initializeSentryReportingWithInjectable = getInjectable({ instantiate: (di): InitializeSentryReportingWith => { const sentryDataSourceName = di.inject(sentryDataSourceNameInjectable); const isProduction = di.inject(isProductionInjectable); + const userStore = di.inject(userStoreInjectable); if (!sentryDataSourceName) { return () => {}; @@ -27,9 +28,6 @@ const initializeSentryReportingWithInjectable = getInjectable({ return (initSentry) => initSentry({ beforeSend: (event) => { - // TODO: remove loading from userStoreInjectable so that this can be moved out - const userStore = di.inject(userStoreInjectable); - if (userStore.allowErrorReporting) { return event; } diff --git a/src/common/user-store/file-name-migration.global-override-for-injectable.ts b/src/common/user-store/file-name-migration.global-override-for-injectable.ts new file mode 100644 index 0000000000..bb0ac054f3 --- /dev/null +++ b/src/common/user-store/file-name-migration.global-override-for-injectable.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getGlobalOverride } from "../test-utils/get-global-override"; +import userStoreFileNameMigrationInjectable from "./file-name-migration.injectable"; + +export default getGlobalOverride(userStoreFileNameMigrationInjectable, () => async () => {}); diff --git a/src/common/user-store/file-name-migration.injectable.ts b/src/common/user-store/file-name-migration.injectable.ts index 8f2dcf1e23..caf94dc491 100644 --- a/src/common/user-store/file-name-migration.injectable.ts +++ b/src/common/user-store/file-name-migration.injectable.ts @@ -9,27 +9,32 @@ import directoryForUserDataInjectable from "../app-paths/directory-for-user-data import { isErrnoException } from "../utils"; import { getInjectable } from "@ogre-tools/injectable"; +export type UserStoreFileNameMigration = () => Promise; + const userStoreFileNameMigrationInjectable = getInjectable({ id: "user-store-file-name-migration", - instantiate: (di) => { + instantiate: (di): UserStoreFileNameMigration => { const userDataPath = di.inject(directoryForUserDataInjectable); const configJsonPath = path.join(userDataPath, "config.json"); const lensUserStoreJsonPath = path.join(userDataPath, "lens-user-store.json"); - try { - fse.moveSync(configJsonPath, lensUserStoreJsonPath); - } catch (error) { - if (error instanceof Error && error.message === "dest already exists.") { - fse.removeSync(configJsonPath); - } else if (isErrnoException(error) && error.code === "ENOENT" && error.path === configJsonPath) { - // (No such file or directory) - return; // file already moved - } else { - // pass other errors along - throw error; + return async () => { + try { + await fse.move(configJsonPath, lensUserStoreJsonPath); + } catch (error) { + if (error instanceof Error && error.message === "dest already exists.") { + await fse.remove(configJsonPath); + } else if (isErrnoException(error) && error.code === "ENOENT" && error.path === configJsonPath) { + // (No such file or directory) + return; // file already moved + } else { + // pass other errors along + throw error; + } } - } + }; }, + causesSideEffects: true, }); export default userStoreFileNameMigrationInjectable; diff --git a/src/common/user-store/user-store.injectable.ts b/src/common/user-store/user-store.injectable.ts index 3b4aba0b56..ffd2310886 100644 --- a/src/common/user-store/user-store.injectable.ts +++ b/src/common/user-store/user-store.injectable.ts @@ -3,8 +3,6 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { ipcMain } from "electron"; -import userStoreFileNameMigrationInjectable from "./file-name-migration.injectable"; import { UserStore } from "./user-store"; import selectedUpdateChannelInjectable from "../application-update/selected-update-channel/selected-update-channel.injectable"; @@ -14,10 +12,6 @@ const userStoreInjectable = getInjectable({ instantiate: (di) => { UserStore.resetInstance(); - if (ipcMain) { - di.inject(userStoreFileNameMigrationInjectable); - } - return UserStore.createInstance({ selectedUpdateChannel: di.inject(selectedUpdateChannelInjectable), }); diff --git a/src/common/user-store/user-store.ts b/src/common/user-store/user-store.ts index 7457548436..6c995996bf 100644 --- a/src/common/user-store/user-store.ts +++ b/src/common/user-store/user-store.ts @@ -34,7 +34,6 @@ export class UserStore extends BaseStore /* implements UserStore }); makeObservable(this); - this.load(); } @observable lastSeenAppVersion = "0.0.0"; diff --git a/src/common/vars/release-channel.injectable.ts b/src/common/vars/release-channel.injectable.ts index 33dd26432a..d8275ff1cb 100644 --- a/src/common/vars/release-channel.injectable.ts +++ b/src/common/vars/release-channel.injectable.ts @@ -2,13 +2,13 @@ * 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 { ReleaseChannel } from "../application-update/update-channels"; +import { createInitializableState } from "../initializable-state/create"; import buildSemanticVersionInjectable from "./build-semantic-version.injectable"; -const releaseChannelInjectable = getInjectable({ +const releaseChannelInjectable = createInitializableState({ id: "release-channel", - instantiate: (di): ReleaseChannel => { + init: (di): ReleaseChannel => { const buildSemanticVersion = di.inject(buildSemanticVersionInjectable); const currentReleaseChannel = buildSemanticVersion.get().prerelease[0]; diff --git a/src/main/application-update/check-for-updates/update-can-be-downgraded.injectable.ts b/src/main/application-update/check-for-updates/update-can-be-downgraded.injectable.ts index aa8fb1b131..b5bdfe2cba 100644 --- a/src/main/application-update/check-for-updates/update-can-be-downgraded.injectable.ts +++ b/src/main/application-update/check-for-updates/update-can-be-downgraded.injectable.ts @@ -16,7 +16,7 @@ const updateCanBeDowngradedInjectable = getInjectable({ return computed(() => ( selectedUpdateChannel.value.get().id === "latest" - && releaseChannel !== "latest" + && releaseChannel.get() !== "latest" )); }, }); diff --git a/src/main/getDiForUnitTesting.ts b/src/main/getDiForUnitTesting.ts index 579e5eafec..8e08c9b86c 100644 --- a/src/main/getDiForUnitTesting.ts +++ b/src/main/getDiForUnitTesting.ts @@ -132,7 +132,11 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) getDisplayIndex: () => "0", }) as unknown as HotbarStore); - di.override(userStoreInjectable, () => ({ startMainReactions: () => {}, extensionRegistryUrl: { customUrl: "some-custom-url" }}) as UserStore); + di.override(userStoreInjectable, () => ({ + startMainReactions: () => {}, + extensionRegistryUrl: { customUrl: "some-custom-url" }, + load: () => {}, + }) as Partial as UserStore); di.override(extensionsStoreInjectable, () => ({ isEnabled: (opts) => (void opts, false) }) as ExtensionsStore); di.override(fileSystemProvisionerStoreInjectable, () => ({}) as FileSystemProvisionerStore); diff --git a/src/main/stores/init-user-store.injectable.ts b/src/main/stores/init-user-store.injectable.ts new file mode 100644 index 0000000000..572c0154e4 --- /dev/null +++ b/src/main/stores/init-user-store.injectable.ts @@ -0,0 +1,28 @@ +/** + * 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 userStoreFileNameMigrationInjectable from "../../common/user-store/file-name-migration.injectable"; +import userStoreInjectable from "../../common/user-store/user-store.injectable"; +import { beforeApplicationIsLoadingInjectionToken } from "../start-main-application/runnable-tokens/before-application-is-loading-injection-token"; +import initDefaultUpdateChannelInjectableInjectable from "../vars/default-update-channel/init.injectable"; + +const initUserStoreInjectable = getInjectable({ + id: "init-user-store", + instantiate: (di) => { + const userStore = di.inject(userStoreInjectable); + const userStoreFileNameMigration = di.inject(userStoreFileNameMigrationInjectable); + + return { + run: async () => { + await userStoreFileNameMigration(); + userStore.load(); + }, + runAfter: di.inject(initDefaultUpdateChannelInjectableInjectable), + }; + }, + injectionToken: beforeApplicationIsLoadingInjectionToken, +}); + +export default initUserStoreInjectable; diff --git a/src/main/vars/default-update-channel/init.injectable.ts b/src/main/vars/default-update-channel/init.injectable.ts index e63b12bac1..e1680efa39 100644 --- a/src/main/vars/default-update-channel/init.injectable.ts +++ b/src/main/vars/default-update-channel/init.injectable.ts @@ -4,8 +4,8 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import defaultUpdateChannelInjectable from "../../../common/application-update/selected-update-channel/default-update-channel.injectable"; -import initSemanticBuildVersionInjectable from "../../../renderer/vars/semantic-build-version/init.injectable"; import { beforeApplicationIsLoadingInjectionToken } from "../../start-main-application/runnable-tokens/before-application-is-loading-injection-token"; +import initReleaseChannelInjectable from "../release-channel/init.injectable"; const initDefaultUpdateChannelInjectableInjectable = getInjectable({ id: "init-default-update-channel-injectable", @@ -14,7 +14,7 @@ const initDefaultUpdateChannelInjectableInjectable = getInjectable({ return { run: () => defaultUpdateChannel.init(), - runAfter: di.inject(initSemanticBuildVersionInjectable), + runAfter: di.inject(initReleaseChannelInjectable), }; }, injectionToken: beforeApplicationIsLoadingInjectionToken, diff --git a/src/main/vars/release-channel/init.injectable.ts b/src/main/vars/release-channel/init.injectable.ts new file mode 100644 index 0000000000..ed358af550 --- /dev/null +++ b/src/main/vars/release-channel/init.injectable.ts @@ -0,0 +1,23 @@ +/** + * 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 releaseChannelInjectable from "../../../common/vars/release-channel.injectable"; +import { beforeApplicationIsLoadingInjectionToken } from "../../start-main-application/runnable-tokens/before-application-is-loading-injection-token"; +import initSemanticBuildVersionInjectable from "../semantic-build-version/init.injectable"; + +const initReleaseChannelInjectable = getInjectable({ + id: "init-release-channel", + instantiate: (di) => { + const releaseChannel = di.inject(releaseChannelInjectable); + + return { + run: () => releaseChannel.init(), + runAfter: di.inject(initSemanticBuildVersionInjectable), + }; + }, + injectionToken: beforeApplicationIsLoadingInjectionToken, +}); + +export default initReleaseChannelInjectable; diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index 2ebd849805..53e2913583 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -28,7 +28,6 @@ import extensionLoaderInjectable from "../extensions/extension-loader/extension- import extensionDiscoveryInjectable from "../extensions/extension-discovery/extension-discovery.injectable"; import extensionInstallationStateStoreInjectable from "../extensions/extension-installation-state-store/extension-installation-state-store.injectable"; import clusterStoreInjectable from "../common/cluster-store/cluster-store.injectable"; -import userStoreInjectable from "../common/user-store/user-store.injectable"; import initRootFrameInjectable from "./frames/root-frame/init-root-frame/init-root-frame.injectable"; import initClusterFrameInjectable from "./frames/cluster-frame/init-cluster-frame/init-cluster-frame.injectable"; import commandOverlayInjectable from "./components/command-palette/command-overlay.injectable"; @@ -92,9 +91,6 @@ export async function bootstrap(di: DiContainer) { */ di.inject(autoRegistrationInjectable); - // TODO: Remove temporal dependencies to make timing of initialization not important - di.inject(userStoreInjectable); - await attachChromeDebugger(); rootElem.classList.toggle("is-mac", isMac); diff --git a/src/renderer/getDiForUnitTesting.tsx b/src/renderer/getDiForUnitTesting.tsx index 5d45d4d3e3..58b17b7eaf 100644 --- a/src/renderer/getDiForUnitTesting.tsx +++ b/src/renderer/getDiForUnitTesting.tsx @@ -66,6 +66,7 @@ import legacyOnChannelListenInjectable from "./ipc/legacy-channel-listen.injecta import getEntitySettingCommandsInjectable from "./components/command-palette/registered-commands/get-entity-setting-commands.injectable"; import storageSaveDelayInjectable from "./utils/create-storage/storage-save-delay.injectable"; import type { GlobalOverride } from "../common/test-utils/get-global-override"; +import type { PartialDeep } from "type-fest"; export const getDiForUnitTesting = ( opts: { doGeneralOverrides?: boolean } = {}, @@ -184,17 +185,14 @@ export const getDiForUnitTesting = ( di.override(defaultShellInjectable, () => "some-default-shell"); - di.override( - userStoreInjectable, - () => - ({ - isTableColumnHidden: () => false, - extensionRegistryUrl: { customUrl: "some-custom-url" }, - syncKubeconfigEntries: observable.map(), - terminalConfig: { fontSize: 42 }, - editorConfiguration: { minimap: {}, tabSize: 42, fontSize: 42 }, - } as unknown as UserStore), - ); + di.override(userStoreInjectable, () => ({ + isTableColumnHidden: () => false, + extensionRegistryUrl: { customUrl: "some-custom-url" }, + syncKubeconfigEntries: observable.map(), + terminalConfig: { fontSize: 42 }, + editorConfiguration: { minimap: {}, tabSize: 42, fontSize: 42 }, + load: () => {}, + } as PartialDeep as UserStore)); di.override(apiManagerInjectable, () => new ApiManager()); diff --git a/src/renderer/stores/init-user-store.injectable.ts b/src/renderer/stores/init-user-store.injectable.ts new file mode 100644 index 0000000000..36f2caf015 --- /dev/null +++ b/src/renderer/stores/init-user-store.injectable.ts @@ -0,0 +1,23 @@ +/** + * 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 userStoreInjectable from "../../common/user-store/user-store.injectable"; +import { beforeFrameStartsInjectionToken } from "../before-frame-starts/before-frame-starts-injection-token"; +import initDefaultUpdateChannelInjectableInjectable from "../vars/default-update-channel/init.injectable"; + +const initUserStoreInjectable = getInjectable({ + id: "init-user-store", + instantiate: (di) => { + const userStore = di.inject(userStoreInjectable); + + return { + run: () => userStore.load(), + runAfter: di.inject(initDefaultUpdateChannelInjectableInjectable), + }; + }, + injectionToken: beforeFrameStartsInjectionToken, +}); + +export default initUserStoreInjectable; diff --git a/src/renderer/vars/default-update-channel/init.injectable.ts b/src/renderer/vars/default-update-channel/init.injectable.ts index fb737c766b..e486be0aaf 100644 --- a/src/renderer/vars/default-update-channel/init.injectable.ts +++ b/src/renderer/vars/default-update-channel/init.injectable.ts @@ -4,8 +4,8 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import defaultUpdateChannelInjectable from "../../../common/application-update/selected-update-channel/default-update-channel.injectable"; -import initSemanticBuildVersionInjectable from "../../../renderer/vars/semantic-build-version/init.injectable"; import { beforeFrameStartsInjectionToken } from "../../before-frame-starts/before-frame-starts-injection-token"; +import initReleaseChannelInjectable from "../release-channel/init.injectable"; const initDefaultUpdateChannelInjectableInjectable = getInjectable({ id: "init-default-update-channel-injectable", @@ -14,7 +14,7 @@ const initDefaultUpdateChannelInjectableInjectable = getInjectable({ return { run: () => defaultUpdateChannel.init(), - runAfter: di.inject(initSemanticBuildVersionInjectable), + runAfter: di.inject(initReleaseChannelInjectable), }; }, injectionToken: beforeFrameStartsInjectionToken, diff --git a/src/renderer/vars/release-channel/init.injectable.ts b/src/renderer/vars/release-channel/init.injectable.ts new file mode 100644 index 0000000000..f384fd2716 --- /dev/null +++ b/src/renderer/vars/release-channel/init.injectable.ts @@ -0,0 +1,23 @@ +/** + * 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 releaseChannelInjectable from "../../../common/vars/release-channel.injectable"; +import { beforeFrameStartsInjectionToken } from "../../before-frame-starts/before-frame-starts-injection-token"; +import initSemanticBuildVersionInjectable from "../semantic-build-version/init.injectable"; + +const initReleaseChannelInjectable = getInjectable({ + id: "init-release-channel", + instantiate: (di) => { + const releaseChannel = di.inject(releaseChannelInjectable); + + return { + run: () => releaseChannel.init(), + runAfter: di.inject(initSemanticBuildVersionInjectable), + }; + }, + injectionToken: beforeFrameStartsInjectionToken, +}); + +export default initReleaseChannelInjectable;