1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Extract responsibilities of WindowManager as injectables

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>
This commit is contained in:
Janne Savolainen 2022-05-02 11:03:03 +03:00
parent 5842db2cc3
commit 5ac9b2aa87
No known key found for this signature in database
GPG Key ID: 8C6CFB2FFFE8F68A
71 changed files with 946 additions and 560 deletions

View File

@ -0,0 +1,14 @@
/**
* 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 { clusterFrameMap } from "./cluster-frames";
const clusterFramesInjectable = getInjectable({
id: "cluster-frames",
instantiate: () => clusterFrameMap,
causesSideEffects: true,
});
export default clusterFramesInjectable;

View File

@ -3,13 +3,13 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import type { DiContainer } from "@ogre-tools/injectable"; import type { DiContainer } from "@ogre-tools/injectable";
import { beforeApplicationIsReadyInjectionToken } from "../../main/start-main-application/before-application-is-ready/before-application-is-ready-injection-token"; import { beforeElectronIsReadyInjectionToken } from "../../main/start-main-application/before-electron-is-ready/before-electron-is-ready-injection-token";
import { afterApplicationIsReadyInjectionToken } from "../../main/start-main-application/after-application-is-ready/after-application-is-ready-injection-token"; import { afterApplicationIsReadyInjectionToken } from "../../main/start-main-application/after-application-is-ready/after-application-is-ready-injection-token";
export const runSetups = async (di: DiContainer) => { export const runSetups = async (di: DiContainer) => {
await Promise.all( await Promise.all(
di di
.injectMany(beforeApplicationIsReadyInjectionToken) .injectMany(beforeElectronIsReadyInjectionToken)
.map((setupable) => setupable.run()), .map((setupable) => setupable.run()),
); );

View File

@ -10,8 +10,8 @@ import type { MenuRegistration } from "../main/menu/menu-registration";
import type { TrayMenuRegistration } from "../main/tray/tray-menu-registration"; import type { TrayMenuRegistration } from "../main/tray/tray-menu-registration";
import type { ShellEnvModifier } from "../main/shell-session/shell-env-modifier/shell-env-modifier-registration"; import type { ShellEnvModifier } from "../main/shell-session/shell-env-modifier/shell-env-modifier-registration";
import { Environments, getEnvironmentSpecificLegacyGlobalDiForExtensionApi } from "./as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; import { Environments, getEnvironmentSpecificLegacyGlobalDiForExtensionApi } from "./as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
import windowManagerInjectable from "../main/window-manager.injectable";
import catalogEntityRegistryInjectable from "../main/catalog/catalog-entity-registry.injectable"; import catalogEntityRegistryInjectable from "../main/catalog/catalog-entity-registry.injectable";
import navigateForExtensionInjectable from "../main/start-main-application/lens-window/navigate-for-extension.injectable";
export class LensMainExtension extends LensExtension { export class LensMainExtension extends LensExtension {
appMenus: MenuRegistration[] = []; appMenus: MenuRegistration[] = [];
@ -35,9 +35,9 @@ export class LensMainExtension extends LensExtension {
async navigate(pageId?: string, params?: Record<string, any>, frameId?: number) { async navigate(pageId?: string, params?: Record<string, any>, frameId?: number) {
const di = getEnvironmentSpecificLegacyGlobalDiForExtensionApi(Environments.main); const di = getEnvironmentSpecificLegacyGlobalDiForExtensionApi(Environments.main);
const windowManager = di.inject(windowManagerInjectable); const navigate = di.inject(navigateForExtensionInjectable);
return windowManager.navigateExtension(this.id, pageId, params, frameId); return navigate(this.id, pageId, params, frameId);
} }
addCatalogSource(id: string, source: IObservableArray<CatalogEntity>) { addCatalogSource(id: string, source: IObservableArray<CatalogEntity>) {

View File

@ -8,12 +8,12 @@ import {
getEnvironmentSpecificLegacyGlobalDiForExtensionApi, getEnvironmentSpecificLegacyGlobalDiForExtensionApi,
} from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; } from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
import windowManagerInjectable from "../../main/window-manager.injectable"; import navigateInjectable from "../../main/start-main-application/lens-window/navigate.injectable";
export function navigate(url: string) { export function navigate(url: string) {
const di = getEnvironmentSpecificLegacyGlobalDiForExtensionApi(Environments.main); const di = getEnvironmentSpecificLegacyGlobalDiForExtensionApi(Environments.main);
const windowManager = di.inject(windowManagerInjectable); const navigate = di.inject(navigateInjectable);
return windowManager.navigate(url); return navigate(url);
} }

View File

@ -16,8 +16,8 @@ import { appPathsIpcChannel } from "../../common/app-paths/app-path-injection-to
import registerChannelInjectable from "./register-channel/register-channel.injectable"; import registerChannelInjectable from "./register-channel/register-channel.injectable";
import joinPathsInjectable from "../../common/path/join-paths.injectable"; import joinPathsInjectable from "../../common/path/join-paths.injectable";
import { import {
beforeApplicationIsReadyInjectionToken, beforeElectronIsReadyInjectionToken,
} from "../start-main-application/before-application-is-ready/before-application-is-ready-injection-token"; } from "../start-main-application/before-electron-is-ready/before-electron-is-ready-injection-token";
const setupAppPathsInjectable = getInjectable({ const setupAppPathsInjectable = getInjectable({
id: "setup-app-paths", id: "setup-app-paths",
@ -54,7 +54,7 @@ const setupAppPathsInjectable = getInjectable({
}; };
}, },
injectionToken: beforeApplicationIsReadyInjectionToken, injectionToken: beforeElectronIsReadyInjectionToken,
}); });
export default setupAppPathsInjectable; export default setupAppPathsInjectable;

View File

@ -6,12 +6,11 @@ import { getInjectable } from "@ogre-tools/injectable";
import electronAppInjectable from "../electron-app.injectable"; import electronAppInjectable from "../electron-app.injectable";
import openDeepLinkInjectable from "../../protocol-handler/lens-protocol-router-main/open-deep-link-for-url/open-deep-link.injectable"; import openDeepLinkInjectable from "../../protocol-handler/lens-protocol-router-main/open-deep-link-for-url/open-deep-link.injectable";
import loggerInjectable from "../../../common/logger.injectable"; import loggerInjectable from "../../../common/logger.injectable";
import { afterApplicationIsReadyInjectionToken } from "../../start-main-application/after-application-is-ready/after-application-is-ready-injection-token";
import commandLineArgumentsInjectable from "../../utils/command-line-arguments.injectable"; import commandLineArgumentsInjectable from "../../utils/command-line-arguments.injectable";
import { pipeline } from "@ogre-tools/fp"; import { pipeline } from "@ogre-tools/fp";
import { find, startsWith, toLower, map } from "lodash/fp"; import { find, startsWith, toLower, map } from "lodash/fp";
import ensureMainWindowInjectable from "../../ensure-main-window/ensure-main-window.injectable"; import { whenApplicationIsLoadingInjectionToken } from "../../start-main-application/when-application-is-loading/when-application-is-loading-injection-token";
import enforceSingleApplicationInstanceInjectable from "./enforce-single-application-instance.injectable"; import applicationWindowInjectable from "../../start-main-application/lens-window/application-window/application-window.injectable";
const setupDeepLinkingInjectable = getInjectable({ const setupDeepLinkingInjectable = getInjectable({
id: "setup-deep-linking", id: "setup-deep-linking",
@ -20,57 +19,52 @@ const setupDeepLinkingInjectable = getInjectable({
const app = di.inject(electronAppInjectable); const app = di.inject(electronAppInjectable);
const logger = di.inject(loggerInjectable); const logger = di.inject(loggerInjectable);
const openDeepLinkForUrl = di.inject(openDeepLinkInjectable); const openDeepLinkForUrl = di.inject(openDeepLinkInjectable);
const applicationWindow = di.inject(applicationWindowInjectable);
const firstInstanceCommandLineArguments = di.inject( const firstInstanceCommandLineArguments = di.inject(
commandLineArgumentsInjectable, commandLineArgumentsInjectable,
); );
const ensureMainWindow = di.inject(ensureMainWindowInjectable);
return { return {
runAfter: di.inject(enforceSingleApplicationInstanceInjectable),
run: async () => { run: async () => {
{ logger.info(`📟 Setting protocol client for lens://`);
logger.info(`📟 Setting protocol client for lens://`);
if (app.setAsDefaultProtocolClient("lens")) { if (app.setAsDefaultProtocolClient("lens")) {
logger.info("📟 Protocol client register succeeded ✅"); logger.info("📟 Protocol client register succeeded ✅");
} else { } else {
logger.info("📟 Protocol client register failed ❗"); logger.info("📟 Protocol client register failed ❗");
}
app.on("open-url", async (event, url) => {
event.preventDefault();
await openDeepLinkForUrl(url);
});
app.on(
"second-instance",
async (_, secondInstanceCommandLineArguments) => {
const url = getDeepLinkUrl(secondInstanceCommandLineArguments);
await ensureMainWindow();
if (url) {
await openDeepLinkForUrl(url);
}
},
);
const url = getDeepLinkUrl(firstInstanceCommandLineArguments);
if (url) {
await openDeepLinkForUrl(url);
}
} }
const url = getDeepLinkUrl(firstInstanceCommandLineArguments);
if (url) {
await openDeepLinkForUrl(url);
}
app.on("open-url", async (event, url) => {
event.preventDefault();
await openDeepLinkForUrl(url);
});
app.on(
"second-instance",
async (_, secondInstanceCommandLineArguments) => {
const url = getDeepLinkUrl(secondInstanceCommandLineArguments);
await applicationWindow.show();
if (url) {
await openDeepLinkForUrl(url);
}
},
);
}, },
}; };
}, },
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: whenApplicationIsLoadingInjectionToken,
}); });
export default setupDeepLinkingInjectable; export default setupDeepLinkingInjectable;

View File

@ -3,8 +3,8 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { afterApplicationIsReadyInjectionToken } from "../../start-main-application/after-application-is-ready/after-application-is-ready-injection-token";
import loggerInjectable from "../../../common/logger.injectable"; import loggerInjectable from "../../../common/logger.injectable";
import { whenApplicationIsLoadingInjectionToken } from "../../start-main-application/when-application-is-loading/when-application-is-loading-injection-token";
const setupDeveloperToolsInDevelopmentEnvironmentInjectable = getInjectable({ const setupDeveloperToolsInDevelopmentEnvironmentInjectable = getInjectable({
id: "setup-developer-tools-in-development-environment", id: "setup-developer-tools-in-development-environment",
@ -34,7 +34,7 @@ const setupDeveloperToolsInDevelopmentEnvironmentInjectable = getInjectable({
}; };
}, },
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: whenApplicationIsLoadingInjectionToken,
}); });
export default setupDeveloperToolsInDevelopmentEnvironmentInjectable; export default setupDeveloperToolsInDevelopmentEnvironmentInjectable;

View File

@ -3,9 +3,9 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { afterApplicationIsReadyInjectionToken } from "../../start-main-application/after-application-is-ready/after-application-is-ready-injection-token";
import powerMonitorInjectable from "../features/power-monitor.injectable"; import powerMonitorInjectable from "../features/power-monitor.injectable";
import exitAppInjectable from "../features/exit-app.injectable"; import exitAppInjectable from "../features/exit-app.injectable";
import { whenApplicationIsLoadingInjectionToken } from "../../start-main-application/when-application-is-loading/when-application-is-loading-injection-token";
const setupDeviceShutdownInjectable = getInjectable({ const setupDeviceShutdownInjectable = getInjectable({
id: "setup-device-shutdown", id: "setup-device-shutdown",
@ -23,7 +23,7 @@ const setupDeviceShutdownInjectable = getInjectable({
}; };
}, },
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: whenApplicationIsLoadingInjectionToken,
}); });
export default setupDeviceShutdownInjectable; export default setupDeviceShutdownInjectable;

View File

@ -9,9 +9,9 @@ import loggerInjectable from "../../../../common/logger.injectable";
import clusterManagerInjectable from "../../../cluster-manager.injectable"; import clusterManagerInjectable from "../../../cluster-manager.injectable";
import applicationMenuItemsInjectable from "../../../menu/application-menu-items.injectable"; import applicationMenuItemsInjectable from "../../../menu/application-menu-items.injectable";
import getAbsolutePathInjectable from "../../../../common/path/get-absolute-path.injectable"; import getAbsolutePathInjectable from "../../../../common/path/get-absolute-path.injectable";
import { afterApplicationIsReadyInjectionToken } from "../../../start-main-application/after-application-is-ready/after-application-is-ready-injection-token";
import catalogEntityRegistryInjectable from "../../../catalog/catalog-entity-registry.injectable"; import catalogEntityRegistryInjectable from "../../../catalog/catalog-entity-registry.injectable";
import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable"; import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable";
import { whenApplicationIsLoadingInjectionToken } from "../../../start-main-application/when-application-is-loading/when-application-is-loading-injection-token";
const setupIpcMainHandlersInjectable = getInjectable({ const setupIpcMainHandlersInjectable = getInjectable({
id: "setup-ipc-main-handlers", id: "setup-ipc-main-handlers",
@ -45,7 +45,7 @@ const setupIpcMainHandlersInjectable = getInjectable({
}; };
}, },
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: whenApplicationIsLoadingInjectionToken,
}); });
export default setupIpcMainHandlersInjectable; export default setupIpcMainHandlersInjectable;

View File

@ -4,16 +4,16 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import electronAppInjectable from "../electron-app.injectable"; import electronAppInjectable from "../electron-app.injectable";
import { afterApplicationIsReadyInjectionToken } from "../../start-main-application/after-application-is-ready/after-application-is-ready-injection-token";
import ensureMainWindowInjectable from "../../ensure-main-window/ensure-main-window.injectable";
import loggerInjectable from "../../../common/logger.injectable"; import loggerInjectable from "../../../common/logger.injectable";
import { whenApplicationIsLoadingInjectionToken } from "../../start-main-application/when-application-is-loading/when-application-is-loading-injection-token";
import applicationWindowInjectable from "../../start-main-application/lens-window/application-window/application-window.injectable";
const setupMainWindowVisibilityAfterActivationInjectable = getInjectable({ const setupMainWindowVisibilityAfterActivationInjectable = getInjectable({
id: "setup-main-window-visibility-after-activation", id: "setup-main-window-visibility-after-activation",
instantiate: (di) => { instantiate: (di) => {
const app = di.inject(electronAppInjectable); const app = di.inject(electronAppInjectable);
const ensureMainWindow = di.inject(ensureMainWindowInjectable); const applicationWindow = di.inject(applicationWindowInjectable);
const logger = di.inject(loggerInjectable); const logger = di.inject(loggerInjectable);
return { return {
@ -22,14 +22,14 @@ const setupMainWindowVisibilityAfterActivationInjectable = getInjectable({
logger.info("APP:ACTIVATE", { hasVisibleWindows: windowIsVisible }); logger.info("APP:ACTIVATE", { hasVisibleWindows: windowIsVisible });
if (!windowIsVisible) { if (!windowIsVisible) {
await ensureMainWindow(false); await applicationWindow.show();
} }
}); });
}, },
}; };
}, },
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: whenApplicationIsLoadingInjectionToken,
}); });
export default setupMainWindowVisibilityAfterActivationInjectable; export default setupMainWindowVisibilityAfterActivationInjectable;

View File

@ -4,7 +4,7 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { broadcastNativeThemeOnUpdate } from "../../native-theme"; import { broadcastNativeThemeOnUpdate } from "../../native-theme";
import { afterApplicationIsReadyInjectionToken } from "../../start-main-application/after-application-is-ready/after-application-is-ready-injection-token"; import { whenApplicationIsLoadingInjectionToken } from "../../start-main-application/when-application-is-loading/when-application-is-loading-injection-token";
const setupOsThemeUpdatesInjectable = getInjectable({ const setupOsThemeUpdatesInjectable = getInjectable({
id: "setup-os-theme-updates", id: "setup-os-theme-updates",
@ -18,7 +18,7 @@ const setupOsThemeUpdatesInjectable = getInjectable({
// Todo: remove explicit usage of IPC to get rid of this. // Todo: remove explicit usage of IPC to get rid of this.
causesSideEffects: true, causesSideEffects: true,
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: whenApplicationIsLoadingInjectionToken,
}); });
export default setupOsThemeUpdatesInjectable; export default setupOsThemeUpdatesInjectable;

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { beforeApplicationIsReadyInjectionToken } from "../../start-main-application/before-application-is-ready/before-application-is-ready-injection-token"; import { beforeElectronIsReadyInjectionToken } from "../../start-main-application/before-electron-is-ready/before-electron-is-ready-injection-token";
import requestSingleInstanceLockInjectable from "../features/request-single-instance-lock.injectable"; import requestSingleInstanceLockInjectable from "../features/request-single-instance-lock.injectable";
import exitAppInjectable from "../features/exit-app.injectable"; import exitAppInjectable from "../features/exit-app.injectable";
@ -23,7 +23,7 @@ const enforceSingleApplicationInstanceInjectable = getInjectable({
}; };
}, },
injectionToken: beforeApplicationIsReadyInjectionToken, injectionToken: beforeElectronIsReadyInjectionToken,
}); });
export default enforceSingleApplicationInstanceInjectable; export default enforceSingleApplicationInstanceInjectable;

View File

@ -4,7 +4,7 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import appNameInjectable from "../../app-paths/app-name/app-name.injectable"; import appNameInjectable from "../../app-paths/app-name/app-name.injectable";
import { beforeApplicationIsReadyInjectionToken } from "../../start-main-application/before-application-is-ready/before-application-is-ready-injection-token"; import { beforeElectronIsReadyInjectionToken } from "../../start-main-application/before-electron-is-ready/before-electron-is-ready-injection-token";
import electronAppInjectable from "../electron-app.injectable"; import electronAppInjectable from "../electron-app.injectable";
const setupApplicationNameInjectable = getInjectable({ const setupApplicationNameInjectable = getInjectable({
@ -21,7 +21,7 @@ const setupApplicationNameInjectable = getInjectable({
}; };
}, },
injectionToken: beforeApplicationIsReadyInjectionToken, injectionToken: beforeElectronIsReadyInjectionToken,
}); });
export default setupApplicationNameInjectable; export default setupApplicationNameInjectable;

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { beforeApplicationIsReadyInjectionToken } from "../../start-main-application/before-application-is-ready/before-application-is-ready-injection-token"; import { beforeElectronIsReadyInjectionToken } from "../../start-main-application/before-electron-is-ready/before-electron-is-ready-injection-token";
import { beforeQuitOfFrontEndInjectionToken } from "../../start-main-application/before-quit-of-front-end/before-quit-of-front-end-injection-token"; import { beforeQuitOfFrontEndInjectionToken } from "../../start-main-application/before-quit-of-front-end/before-quit-of-front-end-injection-token";
import { beforeQuitOfBackEndInjectionToken } from "../../start-main-application/before-quit-of-back-end/before-quit-of-back-end-injection-token"; import { beforeQuitOfBackEndInjectionToken } from "../../start-main-application/before-quit-of-back-end/before-quit-of-back-end-injection-token";
import electronAppInjectable from "../electron-app.injectable"; import electronAppInjectable from "../electron-app.injectable";
@ -54,7 +54,7 @@ const setupRunnablesBeforeClosingOfApplicationInjectable = getInjectable({
}; };
}, },
injectionToken: beforeApplicationIsReadyInjectionToken, injectionToken: beforeElectronIsReadyInjectionToken,
}); });
export default setupRunnablesBeforeClosingOfApplicationInjectable; export default setupRunnablesBeforeClosingOfApplicationInjectable;

View File

@ -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 electronDialogInjectable from "./electron-dialog.injectable";
export type ShowMessagePopup = (title: string, message: string, detail: string) => void;
const showMessagePopupInjectable = getInjectable({
id: "show-message-popup",
instantiate: (di): ShowMessagePopup => {
const dialog = di.inject(electronDialogInjectable);
return async (title, message, detail) => {
await dialog.showMessageBox({
title,
message,
detail,
type: "info",
buttons: ["Close"],
});
};
},
});
export default showMessagePopupInjectable;

View File

@ -1,20 +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 windowManagerInjectable from "../window-manager.injectable";
const ensureMainWindowInjectable = getInjectable({
id: "ensure-main-window",
instantiate: (di) => {
const windowManager = di.inject(windowManagerInjectable);
return async (showSplash = true) => {
await windowManager.ensureMainWindow(showSplash);
};
},
});
export default ensureMainWindowInjectable;

View File

@ -38,25 +38,24 @@ import appEventBusInjectable from "../common/app-event-bus/app-event-bus.injecta
import { EventEmitter } from "../common/event-emitter"; import { EventEmitter } from "../common/event-emitter";
import type { AppEvent } from "../common/app-event-bus/event-bus"; import type { AppEvent } from "../common/app-event-bus/event-bus";
import commandLineArgumentsInjectable from "./utils/command-line-arguments.injectable"; import commandLineArgumentsInjectable from "./utils/command-line-arguments.injectable";
import initializeExtensionsInjectable from "./start-main-application/after-application-is-ready/implementations/initialize-extensions.injectable"; import initializeExtensionsInjectable from "./start-main-application/when-application-is-loading/implementations/initialize-extensions.injectable";
import lensResourcesDirInjectable from "../common/vars/lens-resources-dir.injectable"; import lensResourcesDirInjectable from "../common/vars/lens-resources-dir.injectable";
import registerFileProtocolInjectable from "./electron-app/features/register-file-protocol.injectable"; import registerFileProtocolInjectable from "./electron-app/features/register-file-protocol.injectable";
import environmentVariablesInjectable from "../common/utils/environment-variables.injectable"; import environmentVariablesInjectable from "../common/utils/environment-variables.injectable";
import { CatalogCategoryRegistry } from "../common/catalog"; import { CatalogCategoryRegistry } from "../common/catalog";
import catalogCategoryRegistryInjectable from "../common/catalog/catalog-category-registry.injectable"; import catalogCategoryRegistryInjectable from "../common/catalog/catalog-category-registry.injectable";
import setupIpcMainHandlersInjectable from "./electron-app/after-application-is-ready/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable"; import setupIpcMainHandlersInjectable from "./electron-app/after-application-is-ready/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable";
import setupLensProxyInjectable from "./start-main-application/after-application-is-ready/implementations/setup-lens-proxy.injectable"; import setupLensProxyInjectable from "./start-main-application/before-application-is-loading/implementations/setup-lens-proxy.injectable";
import setupRunnablesForAfterRootFrameIsReadyInjectable from "./start-main-application/after-application-is-ready/implementations/setup-runnables-for-after-root-frame-is-ready.injectable"; import setupRunnablesForAfterRootFrameIsReadyInjectable from "./start-main-application/when-application-is-loading/implementations/setup-runnables-for-after-root-frame-is-ready.injectable";
import setupOsThemeUpdatesInjectable from "./electron-app/after-application-is-ready/setup-os-theme-updates.injectable"; import setupOsThemeUpdatesInjectable from "./electron-app/after-application-is-ready/setup-os-theme-updates.injectable";
import setupSentryInjectable from "./start-main-application/after-application-is-ready/implementations/setup-sentry.injectable"; import setupSentryInjectable from "./start-main-application/when-application-is-loading/implementations/setup-sentry.injectable";
import setupShellInjectable from "./start-main-application/after-application-is-ready/implementations/setup-shell.injectable"; import setupShellInjectable from "./start-main-application/when-application-is-loading/implementations/setup-shell.injectable";
import setupSyncingOfWeblinksInjectable from "./start-main-application/after-application-is-ready/implementations/setup-syncing-of-weblinks.injectable"; import setupSyncingOfWeblinksInjectable from "./start-main-application/when-application-is-loading/implementations/setup-syncing-of-weblinks.injectable";
import stopServicesAndExitAppInjectable from "./stop-services-and-exit-app.injectable"; import stopServicesAndExitAppInjectable from "./stop-services-and-exit-app.injectable";
import trayInjectable from "./tray/tray.injectable"; import trayInjectable from "./tray/tray.injectable";
import applicationMenuInjectable from "./menu/application-menu.injectable"; import applicationMenuInjectable from "./menu/application-menu.injectable";
import windowManagerInjectable from "./window-manager.injectable";
import isDevelopmentInjectable from "../common/vars/is-development.injectable"; import isDevelopmentInjectable from "../common/vars/is-development.injectable";
import setupSystemCaInjectable from "./start-main-application/after-application-is-ready/implementations/setup-system-ca.injectable"; import setupSystemCaInjectable from "./start-main-application/before-application-is-loading/implementations/setup-system-ca.injectable";
import setupDeepLinkingInjectable from "./electron-app/after-application-is-ready/setup-deep-linking.injectable"; import setupDeepLinkingInjectable from "./electron-app/after-application-is-ready/setup-deep-linking.injectable";
import exitAppInjectable from "./electron-app/features/exit-app.injectable"; import exitAppInjectable from "./electron-app/features/exit-app.injectable";
import getCommandLineSwitchInjectable from "./electron-app/features/get-command-line-switch.injectable"; import getCommandLineSwitchInjectable from "./electron-app/features/get-command-line-switch.injectable";
@ -69,6 +68,12 @@ import setupMainWindowVisibilityAfterActivationInjectable from "./electron-app/a
import setupDeviceShutdownInjectable from "./electron-app/after-application-is-ready/setup-device-shutdown.injectable"; import setupDeviceShutdownInjectable from "./electron-app/after-application-is-ready/setup-device-shutdown.injectable";
import setupApplicationNameInjectable from "./electron-app/before-application-is-ready/setup-application-name.injectable"; import setupApplicationNameInjectable from "./electron-app/before-application-is-ready/setup-application-name.injectable";
import setupRunnablesBeforeClosingOfApplicationInjectable from "./electron-app/before-application-is-ready/setup-runnables-before-closing-of-application.injectable"; import setupRunnablesBeforeClosingOfApplicationInjectable from "./electron-app/before-application-is-ready/setup-runnables-before-closing-of-application.injectable";
import showMessagePopupInjectable from "./electron-app/features/show-message-popup.injectable";
import clusterFramesInjectable from "../common/cluster-frames.injectable";
import type { ClusterFrameInfo } from "../common/cluster-frames";
import { observable } from "mobx";
import createBrowserWindowInjectable from "./start-main-application/lens-window/application-window/create-browser-window.injectable";
import type { BrowserWindow } from "electron";
export const getDiForUnitTesting = ( export const getDiForUnitTesting = (
{ doGeneralOverrides } = { doGeneralOverrides: false }, { doGeneralOverrides } = { doGeneralOverrides: false },
@ -98,9 +103,10 @@ export const getDiForUnitTesting = (
di.override(environmentVariablesInjectable, () => ({})); di.override(environmentVariablesInjectable, () => ({}));
di.override(commandLineArgumentsInjectable, () => []); di.override(commandLineArgumentsInjectable, () => []);
di.override(clusterFramesInjectable, () => observable.map<string, ClusterFrameInfo>());
di.override(stopServicesAndExitAppInjectable, () => () => {}); di.override(stopServicesAndExitAppInjectable, () => () => {});
di.override(lensResourcesDirInjectable, () => "/irrelevant"); di.override(lensResourcesDirInjectable, () => "/irrelevant");
di.override(windowManagerInjectable, () => ({ ensureMainWindow: () => Promise.resolve(null) }));
di.override(trayInjectable, () => ({ start: () => {}, stop: () => {} })); di.override(trayInjectable, () => ({ start: () => {}, stop: () => {} }));
di.override(applicationMenuInjectable, () => ({ start: () => {}, stop: () => {} })); di.override(applicationMenuInjectable, () => ({ start: () => {}, stop: () => {} }));
@ -204,6 +210,12 @@ const overrideElectronFeatures = (di: DiContainer) => {
di.override(requestSingleInstanceLockInjectable, () => () => true); di.override(requestSingleInstanceLockInjectable, () => () => true);
di.override(disableHardwareAccelerationInjectable, () => () => {}); di.override(disableHardwareAccelerationInjectable, () => () => {});
di.override(shouldStartHiddenInjectable, () => true); di.override(shouldStartHiddenInjectable, () => true);
di.override(showMessagePopupInjectable, () => () => {});
di.override(createBrowserWindowInjectable, () => async () => ({
show: () => {},
hide: () => {},
}) as unknown as BrowserWindow);
di.override( di.override(
getElectronAppPathInjectable, getElectronAppPathInjectable,

View File

@ -7,12 +7,8 @@ import { checkForUpdates } from "../app-updater";
import { docsUrl, productName, supportUrl } from "../../common/vars"; import { docsUrl, productName, supportUrl } from "../../common/vars";
import { broadcastMessage } from "../../common/ipc"; import { broadcastMessage } from "../../common/ipc";
import { openBrowser } from "../../common/utils"; import { openBrowser } from "../../common/utils";
import { showAbout } from "./menu";
import windowManagerInjectable from "../window-manager.injectable";
import type { MenuItemConstructorOptions } from "electron"; import type { MenuItemConstructorOptions } from "electron";
import { import { webContents } from "electron";
webContents,
} from "electron";
import loggerInjectable from "../../common/logger.injectable"; import loggerInjectable from "../../common/logger.injectable";
import appNameInjectable from "../app-paths/app-name/app-name.injectable"; import appNameInjectable from "../app-paths/app-name/app-name.injectable";
import electronMenuItemsInjectable from "./electron-menu-items.injectable"; import electronMenuItemsInjectable from "./electron-menu-items.injectable";
@ -25,6 +21,9 @@ import navigateToAddClusterInjectable from "../../common/front-end-routing/route
import stopServicesAndExitAppInjectable from "../stop-services-and-exit-app.injectable"; import stopServicesAndExitAppInjectable from "../stop-services-and-exit-app.injectable";
import isMacInjectable from "../../common/vars/is-mac.injectable"; import isMacInjectable from "../../common/vars/is-mac.injectable";
import { computed } from "mobx"; import { computed } from "mobx";
import showAboutInjectable from "./show-about.injectable";
import applicationWindowInjectable from "../start-main-application/lens-window/application-window/application-window.injectable";
import reloadWindowInjectable from "../start-main-application/lens-window/reload-window.injectable";
function ignoreIf(check: boolean, menuItems: MenuItemConstructorOptions[]) { function ignoreIf(check: boolean, menuItems: MenuItemConstructorOptions[]) {
return check ? [] : menuItems; return check ? [] : menuItems;
@ -43,12 +42,14 @@ const applicationMenuItemsInjectable = getInjectable({
const isMac = di.inject(isMacInjectable); const isMac = di.inject(isMacInjectable);
const isAutoUpdateEnabled = di.inject(isAutoUpdateEnabledInjectable); const isAutoUpdateEnabled = di.inject(isAutoUpdateEnabledInjectable);
const electronMenuItems = di.inject(electronMenuItemsInjectable); const electronMenuItems = di.inject(electronMenuItemsInjectable);
const showAbout = di.inject(showAboutInjectable);
const applicationWindow = di.inject(applicationWindowInjectable);
const reloadApplicationWindow = di.inject(reloadWindowInjectable, applicationWindow);
return computed((): MenuItemOpts[] => { return computed((): MenuItemOpts[] => {
// TODO: These injects should happen outside of the computed. // TODO: These injects should happen outside of the computed.
// TODO: Remove temporal dependencies in WindowManager to make sure timing is correct. // TODO: Remove temporal dependencies in WindowManager to make sure timing is correct.
const windowManager = di.inject(windowManagerInjectable);
const stopServicesAndExitApp = di.inject(stopServicesAndExitAppInjectable); const stopServicesAndExitApp = di.inject(stopServicesAndExitAppInjectable);
const navigateToPreferences = di.inject(navigateToPreferencesInjectable); const navigateToPreferences = di.inject(navigateToPreferencesInjectable);
const navigateToExtensions = di.inject(navigateToExtensionsInjectable); const navigateToExtensions = di.inject(navigateToExtensionsInjectable);
@ -75,7 +76,7 @@ const applicationMenuItemsInjectable = getInjectable({
{ {
label: "Check for updates", label: "Check for updates",
click() { click() {
checkForUpdates().then(() => windowManager.ensureMainWindow()); checkForUpdates().then(() => applicationWindow.show());
}, },
}, },
]), ]),
@ -239,7 +240,7 @@ const applicationMenuItemsInjectable = getInjectable({
accelerator: "CmdOrCtrl+R", accelerator: "CmdOrCtrl+R",
id: "reload", id: "reload",
click() { click() {
windowManager.reload(); reloadApplicationWindow();
}, },
}, },
{ role: "toggleDevTools" }, { role: "toggleDevTools" },
@ -293,7 +294,7 @@ const applicationMenuItemsInjectable = getInjectable({
label: "Check for updates", label: "Check for updates",
click() { click() {
checkForUpdates().then(() => checkForUpdates().then(() =>
windowManager.ensureMainWindow(), applicationWindow.show(),
); );
}, },
}, },

View File

@ -2,14 +2,19 @@
* Copyright (c) OpenLens Authors. All rights reserved. * Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { app, dialog, Menu } from "electron"; import { app, Menu } from "electron";
import { appName, isWindows, productName } from "../../common/vars"; import { appName, isWindows, productName } from "../../common/vars";
import packageJson from "../../../package.json"; import packageJson from "../../../package.json";
import type { MenuItemOpts } from "./application-menu-items.injectable"; import type { MenuItemOpts } from "./application-menu-items.injectable";
import type { ShowMessagePopup } from "../electron-app/features/show-message-popup.injectable";
export type MenuTopId = "mac" | "file" | "edit" | "view" | "help"; export type MenuTopId = "mac" | "file" | "edit" | "view" | "help";
export function showAbout() { interface Dependencies {
showMessagePopup: ShowMessagePopup;
}
export const showAbout = ({ showMessagePopup }: Dependencies) => async () => {
const appInfo = [ const appInfo = [
`${appName}: ${app.getVersion()}`, `${appName}: ${app.getVersion()}`,
`Electron: ${process.versions.electron}`, `Electron: ${process.versions.electron}`,
@ -18,14 +23,12 @@ export function showAbout() {
packageJson.copyright, packageJson.copyright,
]; ];
dialog.showMessageBoxSync({ await showMessagePopup(
title: `${isWindows ? " ".repeat(2) : ""}${appName}`, `${isWindows ? " ".repeat(2) : ""}${appName}`,
type: "info", productName,
buttons: ["Close"], appInfo.join("\r\n"),
message: productName, );
detail: appInfo.join("\r\n"), };
});
}
export function buildMenu( export function buildMenu(
applicationMenuItems: MenuItemOpts[], applicationMenuItems: MenuItemOpts[],

View File

@ -0,0 +1,16 @@
/**
* 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 { showAbout } from "./menu";
import showMessagePopupInjectable from "../electron-app/features/show-message-popup.injectable";
const showAboutInjectable = getInjectable({
id: "show-about",
instantiate: (di) =>
showAbout({ showMessagePopup: di.inject(showMessagePopupInjectable) }),
});
export default showAboutInjectable;

View File

@ -3,8 +3,8 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { afterApplicationIsReadyInjectionToken } from "../start-main-application/after-application-is-ready/after-application-is-ready-injection-token";
import applicationMenuInjectable from "./application-menu.injectable"; import applicationMenuInjectable from "./application-menu.injectable";
import { whenApplicationIsLoadingInjectionToken } from "../start-main-application/when-application-is-loading/when-application-is-loading-injection-token";
const startApplicationMenuInjectable = getInjectable({ const startApplicationMenuInjectable = getInjectable({
id: "start-application-menu", id: "start-application-menu",
@ -21,7 +21,7 @@ const startApplicationMenuInjectable = getInjectable({
}; };
}, },
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: whenApplicationIsLoadingInjectionToken,
}); });
export default startApplicationMenuInjectable; export default startApplicationMenuInjectable;

View File

@ -3,17 +3,17 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import windowManagerInjectable from "../window-manager.injectable";
import { navigateToUrlInjectionToken } from "../../common/front-end-routing/navigate-to-url-injection-token"; import { navigateToUrlInjectionToken } from "../../common/front-end-routing/navigate-to-url-injection-token";
import navigateInjectable from "../start-main-application/lens-window/navigate.injectable";
const navigateToUrlInjectable = getInjectable({ const navigateToUrlInjectable = getInjectable({
id: "navigate-to-url", id: "navigate-to-url",
instantiate: (di) => { instantiate: (di) => {
const windowManager = di.inject(windowManagerInjectable); const navigate = di.inject(navigateInjectable);
return (url) => { return (url) => {
windowManager.navigateSync(url); navigate(url);
}; };
}, },

View File

@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
import extensionLoaderInjectable from "../../../extensions/extension-loader/extension-loader.injectable"; import extensionLoaderInjectable from "../../../extensions/extension-loader/extension-loader.injectable";
import { LensProtocolRouterMain } from "./lens-protocol-router-main"; import { LensProtocolRouterMain } from "./lens-protocol-router-main";
import extensionsStoreInjectable from "../../../extensions/extensions-store/extensions-store.injectable"; import extensionsStoreInjectable from "../../../extensions/extensions-store/extensions-store.injectable";
import windowManagerInjectable from "../../window-manager.injectable"; import applicationWindowInjectable from "../../start-main-application/lens-window/application-window/application-window.injectable";
const lensProtocolRouterMainInjectable = getInjectable({ const lensProtocolRouterMainInjectable = getInjectable({
id: "lens-protocol-router-main", id: "lens-protocol-router-main",
@ -15,7 +15,7 @@ const lensProtocolRouterMainInjectable = getInjectable({
new LensProtocolRouterMain({ new LensProtocolRouterMain({
extensionLoader: di.inject(extensionLoaderInjectable), extensionLoader: di.inject(extensionLoaderInjectable),
extensionsStore: di.inject(extensionsStoreInjectable), extensionsStore: di.inject(extensionsStoreInjectable),
windowManager: di.inject(windowManagerInjectable), applicationWindow: di.inject(applicationWindowInjectable),
}), }),
}); });

View File

@ -12,9 +12,11 @@ import { observable, when, makeObservable } from "mobx";
import type { RouteAttempt } from "../../../common/protocol-handler"; import type { RouteAttempt } from "../../../common/protocol-handler";
import { ProtocolHandlerInvalid } from "../../../common/protocol-handler"; import { ProtocolHandlerInvalid } from "../../../common/protocol-handler";
import { disposer, noop } from "../../../common/utils"; import { disposer, noop } from "../../../common/utils";
import type { WindowManager } from "../../window-manager";
import type { ExtensionLoader } from "../../../extensions/extension-loader"; import type { ExtensionLoader } from "../../../extensions/extension-loader";
import type { ExtensionsStore } from "../../../extensions/extensions-store/extensions-store"; import type { ExtensionsStore } from "../../../extensions/extensions-store/extensions-store";
import type {
LensWindow,
} from "../../start-main-application/lens-window/application-window/lens-window-injection-token";
export interface FallbackHandler { export interface FallbackHandler {
(name: string): Promise<boolean>; (name: string): Promise<boolean>;
@ -40,7 +42,7 @@ function checkHost<Query>(url: URLParse<Query>): boolean {
interface Dependencies { interface Dependencies {
extensionLoader: ExtensionLoader; extensionLoader: ExtensionLoader;
extensionsStore: ExtensionsStore; extensionsStore: ExtensionsStore;
windowManager: WindowManager; applicationWindow: LensWindow;
} }
export class LensProtocolRouterMain extends proto.LensProtocolRouter { export class LensProtocolRouterMain extends proto.LensProtocolRouter {
@ -77,7 +79,7 @@ export class LensProtocolRouterMain extends proto.LensProtocolRouter {
throw new proto.RoutingError(proto.RoutingErrorType.INVALID_PROTOCOL, url); throw new proto.RoutingError(proto.RoutingErrorType.INVALID_PROTOCOL, url);
} }
this.dependencies.windowManager.ensureMainWindow().catch(noop); this.dependencies.applicationWindow.show().catch(noop);
const routeInternally = checkHost(url); const routeInternally = checkHost(url);
logger.info(`${proto.LensProtocolRouter.LoggingPrefix}: routing ${url.toString()}`); logger.info(`${proto.LensProtocolRouter.LoggingPrefix}: routing ${url.toString()}`);

View File

@ -9,10 +9,10 @@ const openDeepLinkInjectable = getInjectable({
id: "open-deep-link", id: "open-deep-link",
instantiate: (di) => { instantiate: (di) => {
const protocolRouter = di.inject(lensProtocolRouterMainInjectable); const getProtocolRouter = () => di.inject(lensProtocolRouterMainInjectable);
return async (url: string) => { return async (url: string) => {
await protocolRouter.route(url); await getProtocolRouter().route(url);
}; };
}, },
}); });

View File

@ -0,0 +1,10 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { Runnable } from "../run-many-for";
export const afterApplicationIsLoadedInjectionToken = getInjectionToken<Runnable>({
id: "after-application-is-loaded",
});

View File

@ -3,8 +3,8 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { afterApplicationIsReadyInjectionToken } from "../after-application-is-ready-injection-token";
import appEventBusInjectable from "../../../../common/app-event-bus/app-event-bus.injectable"; import appEventBusInjectable from "../../../../common/app-event-bus/app-event-bus.injectable";
import { afterApplicationIsLoadedInjectionToken } from "../after-application-is-loaded-injection-token";
const emitServiceStartToCommandBusInjectable = getInjectable({ const emitServiceStartToCommandBusInjectable = getInjectable({
id: "emit-service-start-to-command-bus", id: "emit-service-start-to-command-bus",
@ -19,7 +19,7 @@ const emitServiceStartToCommandBusInjectable = getInjectable({
}; };
}, },
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: afterApplicationIsLoadedInjectionToken,
}); });
export default emitServiceStartToCommandBusInjectable; export default emitServiceStartToCommandBusInjectable;

View File

@ -1,36 +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 windowManagerInjectable from "../../../window-manager.injectable";
import loggerInjectable from "../../../../common/logger.injectable";
import { afterApplicationIsReadyInjectionToken } from "../after-application-is-ready-injection-token";
import shouldStartHiddenInjectable from "../../../electron-app/features/should-start-hidden.injectable";
import setupLensProxyInjectable from "./setup-lens-proxy.injectable";
const startMainWindowInjectable = getInjectable({
id: "start-main-window",
instantiate: (di) => {
const windowManager = di.inject(windowManagerInjectable);
const logger = di.inject(loggerInjectable);
const shouldStartHidden = di.inject(shouldStartHiddenInjectable);
return {
runAfter: di.inject(setupLensProxyInjectable),
run: async () => {
logger.info("🖥️ Starting WindowManager");
if (!shouldStartHidden) {
await windowManager.ensureMainWindow();
}
},
};
},
injectionToken: afterApplicationIsReadyInjectionToken,
});
export default startMainWindowInjectable;

View File

@ -0,0 +1,10 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { Runnable } from "../run-many-for";
export const beforeApplicationIsLoadingInjectionToken = getInjectionToken<Runnable>({
id: "before-application-is-loading",
});

View File

@ -3,9 +3,9 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { afterApplicationIsReadyInjectionToken } from "../after-application-is-ready-injection-token";
import registerFileProtocolInjectable from "../../../electron-app/features/register-file-protocol.injectable"; import registerFileProtocolInjectable from "../../../electron-app/features/register-file-protocol.injectable";
import staticFilesDirectoryInjectable from "../../../../common/vars/static-files-directory.injectable"; import staticFilesDirectoryInjectable from "../../../../common/vars/static-files-directory.injectable";
import { beforeApplicationIsLoadingInjectionToken } from "../before-application-is-loading-injection-token";
const setupFileProtocolInjectable = getInjectable({ const setupFileProtocolInjectable = getInjectable({
id: "setup-file-protocol", id: "setup-file-protocol",
@ -21,7 +21,7 @@ const setupFileProtocolInjectable = getInjectable({
}; };
}, },
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: beforeApplicationIsLoadingInjectionToken,
}); });
export default setupFileProtocolInjectable; export default setupFileProtocolInjectable;

View File

@ -3,7 +3,6 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { afterApplicationIsReadyInjectionToken } from "../after-application-is-ready-injection-token";
import { import {
getAppVersion, getAppVersion,
getAppVersionFromProxyServer, getAppVersionFromProxyServer,
@ -14,6 +13,7 @@ import loggerInjectable from "../../../../common/logger.injectable";
import lensProxyPortNumberStateInjectable from "../../../lens-proxy-port-number-state.injectable"; import lensProxyPortNumberStateInjectable from "../../../lens-proxy-port-number-state.injectable";
import isWindowsInjectable from "../../../../common/vars/is-windows.injectable"; import isWindowsInjectable from "../../../../common/vars/is-windows.injectable";
import showErrorPopupInjectable from "../../../electron-app/features/show-error-popup.injectable"; import showErrorPopupInjectable from "../../../electron-app/features/show-error-popup.injectable";
import { beforeApplicationIsLoadingInjectionToken } from "../before-application-is-loading-injection-token";
const setupLensProxyInjectable = getInjectable({ const setupLensProxyInjectable = getInjectable({
id: "setup-lens-proxy", id: "setup-lens-proxy",
@ -74,7 +74,7 @@ const setupLensProxyInjectable = getInjectable({
causesSideEffects: true, causesSideEffects: true,
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: beforeApplicationIsLoadingInjectionToken,
}); });
export default setupLensProxyInjectable; export default setupLensProxyInjectable;

View File

@ -4,7 +4,7 @@
*/ */
import { injectSystemCAs } from "../../../../common/system-ca"; import { injectSystemCAs } from "../../../../common/system-ca";
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { afterApplicationIsReadyInjectionToken } from "../after-application-is-ready-injection-token"; import { beforeApplicationIsLoadingInjectionToken } from "../before-application-is-loading-injection-token";
const setupSystemCaInjectable = getInjectable({ const setupSystemCaInjectable = getInjectable({
id: "setup-system-ca", id: "setup-system-ca",
@ -17,7 +17,7 @@ const setupSystemCaInjectable = getInjectable({
causesSideEffects: true, causesSideEffects: true,
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: beforeApplicationIsLoadingInjectionToken,
}); });
export default setupSystemCaInjectable; export default setupSystemCaInjectable;

View File

@ -5,7 +5,7 @@
import { getInjectionToken } from "@ogre-tools/injectable"; import { getInjectionToken } from "@ogre-tools/injectable";
import type { RunnableSync } from "../run-many-sync-for"; import type { RunnableSync } from "../run-many-sync-for";
export const beforeApplicationIsReadyInjectionToken = export const beforeElectronIsReadyInjectionToken =
getInjectionToken<RunnableSync>({ getInjectionToken<RunnableSync>({
id: "before-application-is-ready", id: "before-electron-is-ready",
}); });

View File

@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import environmentVariablesInjectable from "../../../../common/utils/environment-variables.injectable"; import environmentVariablesInjectable from "../../../../common/utils/environment-variables.injectable";
import disableHardwareAccelerationInjectable from "../../../electron-app/features/disable-hardware-acceleration.injectable"; import disableHardwareAccelerationInjectable from "../../../electron-app/features/disable-hardware-acceleration.injectable";
import { beforeApplicationIsReadyInjectionToken } from "../before-application-is-ready-injection-token"; import { beforeElectronIsReadyInjectionToken } from "../before-electron-is-ready-injection-token";
const setupHardwareAccelerationInjectable = getInjectable({ const setupHardwareAccelerationInjectable = getInjectable({
id: "setup-hardware-acceleration", id: "setup-hardware-acceleration",
@ -23,7 +23,7 @@ const setupHardwareAccelerationInjectable = getInjectable({
}; };
}, },
injectionToken: beforeApplicationIsReadyInjectionToken, injectionToken: beforeElectronIsReadyInjectionToken,
}); });
export default setupHardwareAccelerationInjectable; export default setupHardwareAccelerationInjectable;

View File

@ -4,7 +4,7 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import * as Immer from "immer"; import * as Immer from "immer";
import { beforeApplicationIsReadyInjectionToken } from "../before-application-is-ready-injection-token"; import { beforeElectronIsReadyInjectionToken } from "../before-electron-is-ready-injection-token";
const setupImmerInjectable = getInjectable({ const setupImmerInjectable = getInjectable({
id: "setup-immer", id: "setup-immer",
@ -18,7 +18,7 @@ const setupImmerInjectable = getInjectable({
}, },
}), }),
injectionToken: beforeApplicationIsReadyInjectionToken, injectionToken: beforeElectronIsReadyInjectionToken,
}); });
export default setupImmerInjectable; export default setupImmerInjectable;

View File

@ -4,7 +4,7 @@
*/ */
import * as Mobx from "mobx"; import * as Mobx from "mobx";
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { beforeApplicationIsReadyInjectionToken } from "../before-application-is-ready-injection-token"; import { beforeElectronIsReadyInjectionToken } from "../before-electron-is-ready-injection-token";
const setupMobxInjectable = getInjectable({ const setupMobxInjectable = getInjectable({
id: "setup-mobx", id: "setup-mobx",
@ -23,7 +23,7 @@ const setupMobxInjectable = getInjectable({
}, },
}), }),
injectionToken: beforeApplicationIsReadyInjectionToken, injectionToken: beforeElectronIsReadyInjectionToken,
}); });
export default setupMobxInjectable; export default setupMobxInjectable;

View File

@ -3,7 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { beforeApplicationIsReadyInjectionToken } from "../before-application-is-ready-injection-token"; import { beforeElectronIsReadyInjectionToken } from "../before-electron-is-ready-injection-token";
import getCommandLineSwitchInjectable from "../../../electron-app/features/get-command-line-switch.injectable"; import getCommandLineSwitchInjectable from "../../../electron-app/features/get-command-line-switch.injectable";
const setupProxyEnvInjectable = getInjectable({ const setupProxyEnvInjectable = getInjectable({
@ -37,7 +37,7 @@ const setupProxyEnvInjectable = getInjectable({
}; };
}, },
injectionToken: beforeApplicationIsReadyInjectionToken, injectionToken: beforeElectronIsReadyInjectionToken,
}); });
export default setupProxyEnvInjectable; export default setupProxyEnvInjectable;

View File

@ -0,0 +1,88 @@
/**
* 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 { BrowserWindow } from "electron";
import type {
SendToViewArgs,
} from "../application-window/lens-window-injection-token";
import {
lensWindowInjectionToken,
} from "../application-window/lens-window-injection-token";
const applicationIsLoadingWindowInjectable = getInjectable({
id: "application-is-loading-window",
instantiate: () => {
let loadingWindow: BrowserWindow;
const hideWindow = () => {
loadingWindow?.hide();
};
return {
show: async () => {
if (!loadingWindow) {
loadingWindow = await createLoadingWindow();
}
loadingWindow.show();
},
hide: hideWindow,
close: () => {
hideWindow();
loadingWindow = null;
},
send: async ({ channel, frameInfo, data = [] }: SendToViewArgs) => {
if (!loadingWindow) {
loadingWindow = await createLoadingWindow();
}
if (frameInfo) {
loadingWindow.webContents.sendToFrame(
[frameInfo.processId, frameInfo.frameId],
channel,
...data,
);
} else {
loadingWindow.webContents.send(channel, ...data);
}
},
};
},
injectionToken: lensWindowInjectionToken,
causesSideEffects: true,
});
export default applicationIsLoadingWindowInjectable;
const createLoadingWindow = async () => {
const loadingWindow = new BrowserWindow({
width: 500,
height: 300,
backgroundColor: "#1e2124",
center: true,
frame: false,
resizable: false,
show: false,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
nodeIntegrationInSubFrames: true,
nativeWindowOpen: true,
},
});
await loadingWindow.loadURL("static://splash.html");
return loadingWindow;
};

View File

@ -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 windowStateKeeper from "electron-window-state";
const applicationWindowStateInjectable = getInjectable({
id: "application-window-state",
instantiate: () => {
console.log("asdasd");
return windowStateKeeper({
defaultHeight: 900,
defaultWidth: 1440,
});
},
});
export default applicationWindowStateInjectable;

View File

@ -0,0 +1,61 @@
/**
* 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 createBrowserWindowInjectable from "./create-browser-window.injectable";
import type {
SendToViewArgs } from "./lens-window-injection-token";
import {
lensWindowInjectionToken,
} from "./lens-window-injection-token";
import type { BrowserWindow } from "electron";
import sendToChannelInElectronBrowserWindowInjectable from "./send-to-channel-in-electron-browser-window.injectable";
const applicationWindowInjectable = getInjectable({
id: "application-window",
instantiate: (di) => {
const createBrowserWindow = di.inject(createBrowserWindowInjectable);
let browserWindow: BrowserWindow = null;
const hideWindow = () => {
browserWindow?.hide();
};
const sendToChannelInLensWindow = di.inject(
sendToChannelInElectronBrowserWindowInjectable,
);
return {
show: async () => {
if (!browserWindow) {
browserWindow = await createBrowserWindow("only-application-window");
}
browserWindow.show();
},
hide: hideWindow,
close: () => {
hideWindow();
browserWindow = null;
},
send: async (args: SendToViewArgs) => {
if (!browserWindow) {
browserWindow = await createBrowserWindow("only-application-window");
}
return sendToChannelInLensWindow(browserWindow, args);
},
};
},
injectionToken: lensWindowInjectionToken,
});
export default applicationWindowInjectable;

View File

@ -0,0 +1,158 @@
/**
* 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 loggerInjectable from "../../../../common/logger.injectable";
import appNameInjectable from "../../../app-paths/app-name/app-name.injectable";
import applicationWindowStateInjectable from "./application-window-state.injectable";
import lensProxyPortNumberStateInjectable from "../../../lens-proxy-port-number-state.injectable";
import isMacInjectable from "../../../../common/vars/is-mac.injectable";
import { BrowserWindow, ipcMain } from "electron";
import { delay, openBrowser } from "../../../../common/utils";
import { bundledExtensionsLoaded } from "../../../../common/ipc/extension-handling";
import electronAppInjectable from "../../../electron-app/electron-app.injectable";
import appEventBusInjectable from "../../../../common/app-event-bus/app-event-bus.injectable";
const createBrowserWindowInjectable = getInjectable({
id: "create-browser-window",
instantiate: (di) => {
const logger = di.inject(loggerInjectable);
const applicationName = di.inject(appNameInjectable);
const isMac = di.inject(isMacInjectable);
const appEventBus = di.inject(appEventBusInjectable);
const lensProxyPortNumberState = di.inject(
lensProxyPortNumberStateInjectable,
);
return async (id: string) => {
const applicationWindowState = di.inject(applicationWindowStateInjectable);
const app = di.inject(electronAppInjectable);
const { width, height, x, y } = applicationWindowState;
const browserWindow = new BrowserWindow({
x,
y,
width,
height,
title: applicationName,
show: false,
minWidth: 700, // accommodate 800 x 600 display minimum
minHeight: 500, // accommodate 800 x 600 display minimum
titleBarStyle: isMac ? "hiddenInset" : "hidden",
frame: isMac,
backgroundColor: "#1e2124",
webPreferences: {
nodeIntegration: true,
nodeIntegrationInSubFrames: true,
webviewTag: true,
contextIsolation: false,
nativeWindowOpen: false,
},
});
applicationWindowState.manage(browserWindow);
// open external links in default browser (target=_blank, window.open)
browserWindow
.on("focus", () => {
appEventBus.emit({ name: "app", action: "focus" });
})
.on("blur", () => {
appEventBus.emit({ name: "app", action: "blur" });
})
.on("closed", () => {
// clean up
applicationWindowState.unmanage();
// this.mainWindow = null;
// this.splashWindow = null;
app.dock?.hide(); // hide icon in dock (mac-os)
})
.webContents.on("dom-ready", () => {
appEventBus.emit({ name: "app", action: "dom-ready" });
})
.on("did-fail-load", (_event, code, desc) => {
logger.error(`[WINDOW-MANAGER]: Failed to load Main window`, {
code,
desc,
});
})
.on("did-finish-load", () => {
logger.info("[WINDOW-MANAGER]: Main window loaded");
})
.on("will-attach-webview", (event, webPreferences, params) => {
logger.debug("[WINDOW-MANAGER]: Attaching webview");
// Following is security recommendations because we allow webview tag (webviewTag: true)
// suggested by https://www.electronjs.org/docs/tutorial/security#11-verify-webview-options-before-creation
// and https://www.electronjs.org/docs/tutorial/security#10-do-not-use-allowpopups
if (webPreferences.preload) {
logger.warn(
"[WINDOW-MANAGER]: Strip away preload scripts of webview",
);
delete webPreferences.preload;
}
// @ts-expect-error some electron version uses webPreferences.preloadURL/webPreferences.preload
if (webPreferences.preloadURL) {
logger.warn(
"[WINDOW-MANAGER]: Strip away preload scripts of webview",
);
delete webPreferences.preload;
}
if (params.allowpopups) {
logger.warn(
"[WINDOW-MANAGER]: We do not allow allowpopups props, stop webview from renderer",
);
// event.preventDefault() will destroy the guest page.
event.preventDefault();
return;
}
// Always disable Node.js integration for all webviews
webPreferences.nodeIntegration = false;
})
.setWindowOpenHandler((details) => {
openBrowser(details.url).catch((error) => {
logger.error("[WINDOW-MANAGER]: failed to open browser", { error });
});
return { action: "deny" };
});
const contentUrl = `http://localhost:${lensProxyPortNumberState.get()}`;
logger.info(
`[WINDOW-MANAGER]: Loading Main window from url: ${contentUrl} ...`,
);
await browserWindow.loadURL(contentUrl);
const viewHasLoaded = new Promise<void>((resolve) => {
ipcMain.once(bundledExtensionsLoaded, () => resolve());
});
await viewHasLoaded;
await delay(50); // wait just a bit longer to let the first round of rendering happen
return browserWindow;
};
},
causesSideEffects: true,
});
export default createBrowserWindowInjectable;

View File

@ -0,0 +1,23 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { ClusterFrameInfo } from "../../../../common/cluster-frames";
export interface SendToViewArgs {
channel: string;
frameInfo?: ClusterFrameInfo;
data?: any[];
}
export interface LensWindow {
show: () => Promise<void>;
hide: () => void;
close: () => void;
send: (args: SendToViewArgs) => Promise<void>;
}
export const lensWindowInjectionToken = getInjectionToken<LensWindow>({
id: "lens-window",
});

View File

@ -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 { BrowserWindow } from "electron";
import type { SendToViewArgs } from "./lens-window-injection-token";
const sendToChannelInElectronBrowserWindowInjectable = getInjectable({
id: "send-to-channel-in-electron-browser-window",
instantiate:
() =>
async (
browserWindow: BrowserWindow,
{ channel, frameInfo, data = [] }: SendToViewArgs,
) => {
if (frameInfo) {
browserWindow.webContents.sendToFrame(
[frameInfo.processId, frameInfo.frameId],
channel,
...data,
);
} else {
browserWindow.webContents.send(channel, ...data);
}
},
causesSideEffects: true,
});
export default sendToChannelInElectronBrowserWindowInjectable;

View File

@ -0,0 +1,15 @@
/**
* 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 { observable } from "mobx";
import type { ClusterId } from "../../../../common/cluster-types";
const currentClusterFrameClusterIdStateInjectable = getInjectable({
id: "current-cluster-frame-cluster-id-state",
instantiate: () => observable.box<ClusterId>(),
});
export default currentClusterFrameClusterIdStateInjectable;

View File

@ -0,0 +1,25 @@
/**
* 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 { computed } from "mobx";
import currentClusterFrameClusterIdStateInjectable from "./current-cluster-frame-cluster-id-state.injectable";
import clusterFramesInjectable from "../../../../common/cluster-frames.injectable";
const currentClusterFrameInjectable = getInjectable({
id: "current-cluster-frame",
instantiate: (di) => {
const currentClusterFrameState = di.inject(currentClusterFrameClusterIdStateInjectable);
const clusterFrames = di.inject(clusterFramesInjectable);
return computed(() => {
const clusterId = currentClusterFrameState.get();
return clusterFrames.get(clusterId);
});
},
});
export default currentClusterFrameInjectable;

View File

@ -0,0 +1,32 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { ipcMainOn } from "../../../../common/ipc";
import { IpcRendererNavigationEvents } from "../../../../renderer/navigation/events";
import type { ClusterId } from "../../../../common/cluster-types";
import { getInjectable } from "@ogre-tools/injectable";
import { whenApplicationIsLoadingInjectionToken } from "../../when-application-is-loading/when-application-is-loading-injection-token";
import currentClusterFrameClusterIdStateInjectable from "./current-cluster-frame-cluster-id-state.injectable";
const setupListenerForCurrentClusterFrameInjectable = getInjectable({
id: "setup-listener-for-current-cluster-frame",
instantiate: (di) => ({
run: () => {
const currentClusterFrameState = di.inject(currentClusterFrameClusterIdStateInjectable);
ipcMainOn(
IpcRendererNavigationEvents.CLUSTER_VIEW_CURRENT_ID,
(event, clusterId: ClusterId) => {
currentClusterFrameState.set(clusterId);
},
);
},
}),
injectionToken: whenApplicationIsLoadingInjectionToken,
});
export default setupListenerForCurrentClusterFrameInjectable;

View File

@ -0,0 +1,20 @@
/**
* 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 { lensWindowInjectionToken } from "../application-window/lens-window-injection-token";
const hideAllWindowsInjectable = getInjectable({
id: "hide-all-windows",
instantiate: (di) => () => {
const lensWindows = di.injectMany(lensWindowInjectionToken);
lensWindows.forEach((lensWindow) => {
lensWindow.hide();
});
},
});
export default hideAllWindowsInjectable;

View File

@ -0,0 +1,39 @@
/**
* 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 { iter } from "../../../common/utils";
import applicationWindowInjectable from "./application-window/application-window.injectable";
import clusterFramesInjectable from "../../../common/cluster-frames.injectable";
const navigateForExtensionInjectable = getInjectable({
id: "navigate-for-extension",
instantiate: (di) => {
const applicationWindow = di.inject(applicationWindowInjectable);
const clusterFrames = di.inject(clusterFramesInjectable);
return async (
extId: string,
pageId?: string,
params?: Record<string, any>,
frameId?: number,
) => {
await applicationWindow.show();
const frameInfo = iter.find(
clusterFrames.values(),
(frameInfo) => frameInfo.frameId === frameId,
);
await applicationWindow.send({
channel: "extension:navigate",
frameInfo,
data: [extId, pageId, params],
});
};
},
});
export default navigateForExtensionInjectable;

View File

@ -0,0 +1,39 @@
/**
* 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 { iter } from "../../../common/utils";
import applicationWindowInjectable from "./application-window/application-window.injectable";
import clusterFramesInjectable from "../../../common/cluster-frames.injectable";
import { IpcRendererNavigationEvents } from "../../../renderer/navigation/events";
const navigateInjectable = getInjectable({
id: "navigate",
instantiate: (di) => {
const applicationWindow = di.inject(applicationWindowInjectable);
const clusterFrames = di.inject(clusterFramesInjectable);
return async (url: string, frameId?: number) => {
await applicationWindow.show();
const frameInfo = iter.find(
clusterFrames.values(),
(frameInfo) => frameInfo.frameId === frameId,
);
const channel = frameInfo
? IpcRendererNavigationEvents.NAVIGATE_IN_CLUSTER
: IpcRendererNavigationEvents.NAVIGATE_IN_APP;
await applicationWindow.send({
channel,
frameInfo,
data: [url],
});
};
},
});
export default navigateInjectable;

View File

@ -0,0 +1,24 @@
/**
* 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 { webContents } from "electron";
const reloadAllWindowsInjectable = getInjectable({
id: "reload-all-windows",
instantiate: () => () => {
webContents
.getAllWebContents()
.filter((wc) => wc.getType() === "window")
.forEach((wc) => {
wc.reload();
wc.clearHistory();
});
},
causesSideEffects: true,
});
export default reloadAllWindowsInjectable;

View File

@ -0,0 +1,33 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
import type { LensWindow } from "./application-window/lens-window-injection-token";
import { IpcRendererNavigationEvents } from "../../../renderer/navigation/events";
import currentClusterFrameInjectable from "./current-cluster-frame/current-cluster-frame.injectable";
import reloadAllWindowsInjectable from "./reload-all-windows.injectable";
const reloadWindowInjectable = getInjectable({
id: "reload-window",
instantiate: (di, lensWindow: LensWindow) => () => {
const currentClusterIframe = di.inject(currentClusterFrameInjectable);
const reloadAllWindows = di.inject(reloadAllWindowsInjectable);
const frameInfo = currentClusterIframe.get();
if (frameInfo) {
lensWindow.send({
channel: IpcRendererNavigationEvents.RELOAD_PAGE,
frameInfo,
});
} else {
reloadAllWindows();
}
},
lifecycle: lifecycleEnum.transient,
});
export default reloadWindowInjectable;

View File

@ -5,10 +5,20 @@
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import electronAppInjectable from "../electron-app/electron-app.injectable"; import electronAppInjectable from "../electron-app/electron-app.injectable";
import { beforeApplicationIsReadyInjectionToken } from "./before-application-is-ready/before-application-is-ready-injection-token";
import { afterApplicationIsReadyInjectionToken } from "./after-application-is-ready/after-application-is-ready-injection-token";
import { runManyFor } from "./run-many-for"; import { runManyFor } from "./run-many-for";
import { runManySyncFor } from "./run-many-sync-for"; import { runManySyncFor } from "./run-many-sync-for";
import { beforeElectronIsReadyInjectionToken } from "./before-electron-is-ready/before-electron-is-ready-injection-token";
import { beforeApplicationIsLoadingInjectionToken } from "./before-application-is-loading/before-application-is-loading-injection-token";
import { whenApplicationIsLoadingInjectionToken } from "./when-application-is-loading/when-application-is-loading-injection-token";
import { afterApplicationIsLoadedInjectionToken } from "./after-application-is-loaded/after-application-is-loaded-injection-token";
import applicationIsLoadingWindowInjectable from "./lens-window/application-is-loading-window/application-is-loading-window.injectable";
import applicationWindowInjectable from "./lens-window/application-window/application-window.injectable";
import shouldStartHiddenInjectable from "../electron-app/features/should-start-hidden.injectable";
import openDeepLinkInjectable from "../protocol-handler/lens-protocol-router-main/open-deep-link-for-url/open-deep-link.injectable";
import { pipeline } from "@ogre-tools/fp";
import { find, map, startsWith, toLower } from "lodash/fp";
import commandLineArgumentsInjectable from "../utils/command-line-arguments.injectable";
const startMainApplicationInjectable = getInjectable({ const startMainApplicationInjectable = getInjectable({
id: "start-main-application", id: "start-main-application",
@ -16,21 +26,52 @@ const startMainApplicationInjectable = getInjectable({
instantiate: (di) => { instantiate: (di) => {
const runMany = runManyFor(di); const runMany = runManyFor(di);
const runManySync = runManySyncFor(di); const runManySync = runManySyncFor(di);
const app = di.inject(electronAppInjectable); const electronApp = di.inject(electronAppInjectable);
const applicationWindow = di.inject(applicationWindowInjectable);
const applicationIsLoadingWindow = di.inject(applicationIsLoadingWindowInjectable);
const shouldStartHidden = di.inject(shouldStartHiddenInjectable);
const openDeepLink = di.inject(openDeepLinkInjectable);
const commandLineArguments = di.inject(commandLineArgumentsInjectable);
const beforeElectronIsReady = runManySync(beforeElectronIsReadyInjectionToken);
const beforeApplicationIsLoading = runMany(beforeApplicationIsLoadingInjectionToken);
const whenApplicationIsLoading = runMany(whenApplicationIsLoadingInjectionToken);
const afterApplicationIsLoaded = runMany(afterApplicationIsLoadedInjectionToken);
return async () => { return async () => {
// Stuff happening before application is ready needs to be synchronous because of // Stuff happening before application is ready needs to be synchronous because of
// https://github.com/electron/electron/issues/21370 // https://github.com/electron/electron/issues/21370
runManySync(beforeApplicationIsReadyInjectionToken)(); beforeElectronIsReady();
await app.whenReady(); await electronApp.whenReady();
await runMany(afterApplicationIsReadyInjectionToken)(); await beforeApplicationIsLoading();
if (!shouldStartHidden) {
await applicationIsLoadingWindow.show();
}
await whenApplicationIsLoading();
if (!shouldStartHidden) {
const url = getDeepLinkUrl(commandLineArguments);
if (url) {
await openDeepLink(url);
} else {
await applicationWindow.show();
}
applicationIsLoadingWindow.close();
}
await afterApplicationIsLoaded();
}; };
}, },
}); });
const getDeepLinkUrl = (commandLineArguments: string[]) =>
pipeline(commandLineArguments, map(toLower), find(startsWith("lens://")));
export default startMainApplicationInjectable; export default startMainApplicationInjectable;

View File

@ -3,13 +3,13 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { afterApplicationIsReadyInjectionToken } from "../after-application-is-ready-injection-token";
import type { InstalledExtension } from "../../../../extensions/extension-discovery/extension-discovery"; import type { InstalledExtension } from "../../../../extensions/extension-discovery/extension-discovery";
import type { LensExtensionId } from "../../../../extensions/lens-extension"; import type { LensExtensionId } from "../../../../extensions/lens-extension";
import loggerInjectable from "../../../../common/logger.injectable"; import loggerInjectable from "../../../../common/logger.injectable";
import extensionDiscoveryInjectable from "../../../../extensions/extension-discovery/extension-discovery.injectable"; import extensionDiscoveryInjectable from "../../../../extensions/extension-discovery/extension-discovery.injectable";
import extensionLoaderInjectable from "../../../../extensions/extension-loader/extension-loader.injectable"; import extensionLoaderInjectable from "../../../../extensions/extension-loader/extension-loader.injectable";
import showErrorPopupInjectable from "../../../electron-app/features/show-error-popup.injectable"; import showErrorPopupInjectable from "../../../electron-app/features/show-error-popup.injectable";
import { whenApplicationIsLoadingInjectionToken } from "../when-application-is-loading-injection-token";
const initializeExtensionsInjectable = getInjectable({ const initializeExtensionsInjectable = getInjectable({
id: "initialize-extensions", id: "initialize-extensions",
@ -61,7 +61,7 @@ const initializeExtensionsInjectable = getInjectable({
causesSideEffects: true, causesSideEffects: true,
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: whenApplicationIsLoadingInjectionToken,
}); });
export default initializeExtensionsInjectable; export default initializeExtensionsInjectable;

View File

@ -3,13 +3,13 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { afterApplicationIsReadyInjectionToken } from "../after-application-is-ready-injection-token";
import { ClusterIdDetector } from "../../../cluster-detectors/cluster-id-detector"; import { ClusterIdDetector } from "../../../cluster-detectors/cluster-id-detector";
import { LastSeenDetector } from "../../../cluster-detectors/last-seen-detector"; import { LastSeenDetector } from "../../../cluster-detectors/last-seen-detector";
import { VersionDetector } from "../../../cluster-detectors/version-detector"; import { VersionDetector } from "../../../cluster-detectors/version-detector";
import { DistributionDetector } from "../../../cluster-detectors/distribution-detector"; import { DistributionDetector } from "../../../cluster-detectors/distribution-detector";
import { NodesCountDetector } from "../../../cluster-detectors/nodes-count-detector"; import { NodesCountDetector } from "../../../cluster-detectors/nodes-count-detector";
import detectorRegistryInjectable from "../../../cluster-detectors/detector-registry.injectable"; import detectorRegistryInjectable from "../../../cluster-detectors/detector-registry.injectable";
import { whenApplicationIsLoadingInjectionToken } from "../when-application-is-loading-injection-token";
const setupDetectorRegistryInjectable = getInjectable({ const setupDetectorRegistryInjectable = getInjectable({
id: "setup-detector-registry", id: "setup-detector-registry",
@ -29,7 +29,7 @@ const setupDetectorRegistryInjectable = getInjectable({
}; };
}, },
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: whenApplicationIsLoadingInjectionToken,
}); });
export default setupDetectorRegistryInjectable; export default setupDetectorRegistryInjectable;

View File

@ -3,9 +3,9 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { afterApplicationIsReadyInjectionToken } from "../after-application-is-ready-injection-token";
import hotbarStoreInjectable from "../../../../common/hotbar-store.injectable"; import hotbarStoreInjectable from "../../../../common/hotbar-store.injectable";
import setupSyncingOfGeneralCatalogEntitiesInjectable from "./setup-syncing-of-general-catalog-entities.injectable"; import setupSyncingOfGeneralCatalogEntitiesInjectable from "./setup-syncing-of-general-catalog-entities.injectable";
import { whenApplicationIsLoadingInjectionToken } from "../when-application-is-loading-injection-token";
const setupHotbarStoreInjectable = getInjectable({ const setupHotbarStoreInjectable = getInjectable({
id: "setup-hotbar-store", id: "setup-hotbar-store",
@ -20,7 +20,7 @@ const setupHotbarStoreInjectable = getInjectable({
runAfter: di.inject(setupSyncingOfGeneralCatalogEntitiesInjectable), runAfter: di.inject(setupSyncingOfGeneralCatalogEntitiesInjectable),
}), }),
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: whenApplicationIsLoadingInjectionToken,
}); });
export default setupHotbarStoreInjectable; export default setupHotbarStoreInjectable;

View File

@ -3,12 +3,12 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { afterApplicationIsReadyInjectionToken } from "../after-application-is-ready-injection-token";
import { PrometheusLens } from "../../../prometheus/lens"; import { PrometheusLens } from "../../../prometheus/lens";
import { PrometheusHelm } from "../../../prometheus/helm"; import { PrometheusHelm } from "../../../prometheus/helm";
import { PrometheusOperator } from "../../../prometheus/operator"; import { PrometheusOperator } from "../../../prometheus/operator";
import { PrometheusStacklight } from "../../../prometheus/stacklight"; import { PrometheusStacklight } from "../../../prometheus/stacklight";
import prometheusProviderRegistryInjectable from "../../../prometheus/prometheus-provider-registry.injectable"; import prometheusProviderRegistryInjectable from "../../../prometheus/prometheus-provider-registry.injectable";
import { whenApplicationIsLoadingInjectionToken } from "../when-application-is-loading-injection-token";
const setupPrometheusRegistryInjectable = getInjectable({ const setupPrometheusRegistryInjectable = getInjectable({
id: "setup-prometheus-registry", id: "setup-prometheus-registry",
@ -27,7 +27,7 @@ const setupPrometheusRegistryInjectable = getInjectable({
}; };
}, },
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: whenApplicationIsLoadingInjectionToken,
}); });
export default setupPrometheusRegistryInjectable; export default setupPrometheusRegistryInjectable;

View File

@ -3,8 +3,8 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { afterApplicationIsReadyInjectionToken } from "../after-application-is-ready-injection-token";
import userStoreInjectable from "../../../../common/user-store/user-store.injectable"; import userStoreInjectable from "../../../../common/user-store/user-store.injectable";
import { whenApplicationIsLoadingInjectionToken } from "../when-application-is-loading-injection-token";
const setupReactionsInUserStoreInjectable = getInjectable({ const setupReactionsInUserStoreInjectable = getInjectable({
id: "setup-reactions-in-user-store", id: "setup-reactions-in-user-store",
@ -19,7 +19,7 @@ const setupReactionsInUserStoreInjectable = getInjectable({
}; };
}, },
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: whenApplicationIsLoadingInjectionToken,
}); });
export default setupReactionsInUserStoreInjectable; export default setupReactionsInUserStoreInjectable;

View File

@ -6,8 +6,8 @@ import { getInjectable } from "@ogre-tools/injectable";
import { ipcMainOn } from "../../../../common/ipc"; import { ipcMainOn } from "../../../../common/ipc";
import { IpcRendererNavigationEvents } from "../../../../renderer/navigation/events"; import { IpcRendererNavigationEvents } from "../../../../renderer/navigation/events";
import { afterRootFrameIsReadyInjectionToken } from "../../after-root-frame-is-ready/after-root-frame-is-ready-injection-token"; import { afterRootFrameIsReadyInjectionToken } from "../../after-root-frame-is-ready/after-root-frame-is-ready-injection-token";
import { afterApplicationIsReadyInjectionToken } from "../after-application-is-ready-injection-token";
import { runManyFor } from "../../run-many-for"; import { runManyFor } from "../../run-many-for";
import { whenApplicationIsLoadingInjectionToken } from "../when-application-is-loading-injection-token";
const setupRunnablesForAfterRootFrameIsReadyInjectable = getInjectable({ const setupRunnablesForAfterRootFrameIsReadyInjectable = getInjectable({
id: "setup-runnables-for-after-root-frame-is-ready", id: "setup-runnables-for-after-root-frame-is-ready",
@ -31,7 +31,7 @@ const setupRunnablesForAfterRootFrameIsReadyInjectable = getInjectable({
// Direct usage of IPC // Direct usage of IPC
causesSideEffects: true, causesSideEffects: true,
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: whenApplicationIsLoadingInjectionToken,
}); });
export default setupRunnablesForAfterRootFrameIsReadyInjectable; export default setupRunnablesForAfterRootFrameIsReadyInjectable;

View File

@ -3,9 +3,9 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { afterApplicationIsReadyInjectionToken } from "../after-application-is-ready-injection-token";
import { initializeSentryReporting } from "../../../../common/sentry"; import { initializeSentryReporting } from "../../../../common/sentry";
import { init } from "@sentry/electron/main"; import { init } from "@sentry/electron/main";
import { whenApplicationIsLoadingInjectionToken } from "../when-application-is-loading-injection-token";
const setupSentryInjectable = getInjectable({ const setupSentryInjectable = getInjectable({
id: "setup-sentry", id: "setup-sentry",
@ -18,7 +18,7 @@ const setupSentryInjectable = getInjectable({
causesSideEffects: true, causesSideEffects: true,
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: whenApplicationIsLoadingInjectionToken,
}); });
export default setupSentryInjectable; export default setupSentryInjectable;

View File

@ -3,9 +3,9 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { afterApplicationIsReadyInjectionToken } from "../after-application-is-ready-injection-token";
import { shellSync } from "../../../shell-sync"; import { shellSync } from "../../../shell-sync";
import loggerInjectable from "../../../../common/logger.injectable"; import loggerInjectable from "../../../../common/logger.injectable";
import { whenApplicationIsLoadingInjectionToken } from "../when-application-is-loading-injection-token";
const setupShellInjectable = getInjectable({ const setupShellInjectable = getInjectable({
id: "setup-shell", id: "setup-shell",
@ -22,7 +22,7 @@ const setupShellInjectable = getInjectable({
}; };
}, },
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: whenApplicationIsLoadingInjectionToken,
causesSideEffects: true, causesSideEffects: true,
}); });

View File

@ -3,8 +3,8 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { afterApplicationIsReadyInjectionToken } from "../after-application-is-ready-injection-token";
import syncGeneralCatalogEntitiesInjectable from "../../../catalog-sources/sync-general-catalog-entities.injectable"; import syncGeneralCatalogEntitiesInjectable from "../../../catalog-sources/sync-general-catalog-entities.injectable";
import { whenApplicationIsLoadingInjectionToken } from "../when-application-is-loading-injection-token";
const setupSyncingOfGeneralCatalogEntitiesInjectable = getInjectable({ const setupSyncingOfGeneralCatalogEntitiesInjectable = getInjectable({
id: "setup-syncing-of-general-catalog-entities", id: "setup-syncing-of-general-catalog-entities",
@ -21,7 +21,7 @@ const setupSyncingOfGeneralCatalogEntitiesInjectable = getInjectable({
}; };
}, },
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: whenApplicationIsLoadingInjectionToken,
}); });
export default setupSyncingOfGeneralCatalogEntitiesInjectable; export default setupSyncingOfGeneralCatalogEntitiesInjectable;

View File

@ -3,10 +3,10 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { afterApplicationIsReadyInjectionToken } from "../after-application-is-ready-injection-token";
import { syncWeblinks } from "../../../catalog-sources"; import { syncWeblinks } from "../../../catalog-sources";
import weblinkStoreInjectable from "../../../../common/weblink-store.injectable"; import weblinkStoreInjectable from "../../../../common/weblink-store.injectable";
import catalogEntityRegistryInjectable from "../../../catalog/catalog-entity-registry.injectable"; import catalogEntityRegistryInjectable from "../../../catalog/catalog-entity-registry.injectable";
import { whenApplicationIsLoadingInjectionToken } from "../when-application-is-loading-injection-token";
const setupSyncingOfWeblinksInjectable = getInjectable({ const setupSyncingOfWeblinksInjectable = getInjectable({
id: "setup-syncing-of-weblinks", id: "setup-syncing-of-weblinks",
@ -22,7 +22,7 @@ const setupSyncingOfWeblinksInjectable = getInjectable({
}; };
}, },
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: whenApplicationIsLoadingInjectionToken,
}); });
export default setupSyncingOfWeblinksInjectable; export default setupSyncingOfWeblinksInjectable;

View File

@ -0,0 +1,10 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { Runnable } from "../run-many-for";
export const whenApplicationIsLoadingInjectionToken = getInjectionToken<Runnable>({
id: "when-application-is-loading",
});

View File

@ -5,23 +5,23 @@
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import exitAppInjectable from "./electron-app/features/exit-app.injectable"; import exitAppInjectable from "./electron-app/features/exit-app.injectable";
import clusterManagerInjectable from "./cluster-manager.injectable"; import clusterManagerInjectable from "./cluster-manager.injectable";
import windowManagerInjectable from "./window-manager.injectable";
import appEventBusInjectable from "../common/app-event-bus/app-event-bus.injectable"; import appEventBusInjectable from "../common/app-event-bus/app-event-bus.injectable";
import loggerInjectable from "../common/logger.injectable"; import loggerInjectable from "../common/logger.injectable";
import hideAllWindowsInjectable from "./start-main-application/lens-window/hide-all-windows/hide-all-windows.injectable";
const stopServicesAndExitAppInjectable = getInjectable({ const stopServicesAndExitAppInjectable = getInjectable({
id: "stop-services-and-exit-app", id: "stop-services-and-exit-app",
instantiate: (di) => { instantiate: (di) => {
const exitApp = di.inject(exitAppInjectable); const exitApp = di.inject(exitAppInjectable);
const windowManager = di.inject(windowManagerInjectable);
const clusterManager = di.inject(clusterManagerInjectable); const clusterManager = di.inject(clusterManagerInjectable);
const appEventBus = di.inject(appEventBusInjectable); const appEventBus = di.inject(appEventBusInjectable);
const logger = di.inject(loggerInjectable); const logger = di.inject(loggerInjectable);
const hideAllWindows = di.inject(hideAllWindowsInjectable);
return () => { return () => {
appEventBus.emit({ name: "service", action: "close" }); appEventBus.emit({ name: "service", action: "close" });
windowManager.hide(); hideAllWindows();
clusterManager.stop(); clusterManager.stop();
logger.info("SERVICE:QUIT"); logger.info("SERVICE:QUIT");
setTimeout(exitApp, 1000); setTimeout(exitApp, 1000);

View File

@ -3,8 +3,8 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { afterApplicationIsReadyInjectionToken } from "../start-main-application/after-application-is-ready/after-application-is-ready-injection-token";
import trayInjectable from "./tray.injectable"; import trayInjectable from "./tray.injectable";
import { whenApplicationIsLoadingInjectionToken } from "../start-main-application/when-application-is-loading/when-application-is-loading-injection-token";
const startTrayInjectable = getInjectable({ const startTrayInjectable = getInjectable({
id: "start-tray", id: "start-tray",
@ -19,7 +19,7 @@ const startTrayInjectable = getInjectable({
}; };
}, },
injectionToken: afterApplicationIsReadyInjectionToken, injectionToken: whenApplicationIsLoadingInjectionToken,
}); });
export default startTrayInjectable; export default startTrayInjectable;

View File

@ -4,33 +4,36 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { initTray } from "./tray"; import { initTray } from "./tray";
import windowManagerInjectable from "../window-manager.injectable";
import trayMenuItemsInjectable from "./tray-menu-items.injectable"; import trayMenuItemsInjectable from "./tray-menu-items.injectable";
import navigateToPreferencesInjectable from "../../common/front-end-routing/routes/preferences/navigate-to-preferences.injectable"; import navigateToPreferencesInjectable from "../../common/front-end-routing/routes/preferences/navigate-to-preferences.injectable";
import stopServicesAndExitAppInjectable from "../stop-services-and-exit-app.injectable"; import stopServicesAndExitAppInjectable from "../stop-services-and-exit-app.injectable";
import { getStartableStoppable } from "../../common/utils/get-startable-stoppable"; import { getStartableStoppable } from "../../common/utils/get-startable-stoppable";
import isAutoUpdateEnabledInjectable from "../is-auto-update-enabled.injectable"; import isAutoUpdateEnabledInjectable from "../is-auto-update-enabled.injectable";
import trayIconPathInjectable from "./tray-icon-path.injectable"; import trayIconPathInjectable from "./tray-icon-path.injectable";
import applicationWindowInjectable from "../start-main-application/lens-window/application-window/application-window.injectable";
import showAboutInjectable from "../menu/show-about.injectable";
const trayInjectable = getInjectable({ const trayInjectable = getInjectable({
id: "tray", id: "tray",
instantiate: (di) => { instantiate: (di) => {
const windowManager = di.inject(windowManagerInjectable);
const trayMenuItems = di.inject(trayMenuItemsInjectable); const trayMenuItems = di.inject(trayMenuItemsInjectable);
const navigateToPreferences = di.inject(navigateToPreferencesInjectable); const navigateToPreferences = di.inject(navigateToPreferencesInjectable);
const stopServicesAndExitApp = di.inject(stopServicesAndExitAppInjectable); const stopServicesAndExitApp = di.inject(stopServicesAndExitAppInjectable);
const isAutoUpdateEnabled = di.inject(isAutoUpdateEnabledInjectable); const isAutoUpdateEnabled = di.inject(isAutoUpdateEnabledInjectable);
const trayIconPath = di.inject(trayIconPathInjectable); const trayIconPath = di.inject(trayIconPathInjectable);
const applicationWindow = di.inject(applicationWindowInjectable);
const showAboutPopup = di.inject(showAboutInjectable);
return getStartableStoppable("build-of-tray", () => return getStartableStoppable("build-of-tray", () =>
initTray( initTray(
windowManager,
trayMenuItems, trayMenuItems,
navigateToPreferences, navigateToPreferences,
stopServicesAndExitApp, stopServicesAndExitApp,
isAutoUpdateEnabled, isAutoUpdateEnabled,
trayIconPath, trayIconPath,
applicationWindow,
showAboutPopup,
), ),
); );
}, },

View File

@ -7,15 +7,14 @@ import packageInfo from "../../../package.json";
import { Menu, Tray } from "electron"; import { Menu, Tray } from "electron";
import type { IComputedValue } from "mobx"; import type { IComputedValue } from "mobx";
import { autorun } from "mobx"; import { autorun } from "mobx";
import { showAbout } from "../menu/menu";
import { checkForUpdates } from "../app-updater"; import { checkForUpdates } from "../app-updater";
import type { WindowManager } from "../window-manager";
import logger from "../logger"; import logger from "../logger";
import { isDevelopment, isWindows, productName, staticFilesDirectory } from "../../common/vars"; import { isDevelopment, isWindows, productName, staticFilesDirectory } from "../../common/vars";
import type { Disposer } from "../../common/utils"; import type { Disposer } from "../../common/utils";
import { disposer, toJS } from "../../common/utils"; import { disposer, toJS } from "../../common/utils";
import type { TrayMenuRegistration } from "./tray-menu-registration"; import type { TrayMenuRegistration } from "./tray-menu-registration";
import path from "path"; import path from "path";
import type { LensWindow } from "../start-main-application/lens-window/application-window/lens-window-injection-token";
const TRAY_LOG_PREFIX = "[TRAY]"; const TRAY_LOG_PREFIX = "[TRAY]";
@ -31,11 +30,12 @@ function getTrayIconPath(): string {
} }
export function initTray( export function initTray(
windowManager: WindowManager,
trayMenuItems: IComputedValue<TrayMenuRegistration[]>, trayMenuItems: IComputedValue<TrayMenuRegistration[]>,
navigateToPreferences: () => void, navigateToPreferences: () => void,
stopServicesAndExitApp: () => void, stopServicesAndExitApp: () => void,
isAutoUpdateEnabled: () => boolean, isAutoUpdateEnabled: () => boolean,
applicationWindow: LensWindow,
showAbout: () => void,
): Disposer { ): Disposer {
const icon = getTrayIconPath(); const icon = getTrayIconPath();
@ -45,8 +45,7 @@ export function initTray(
if (isWindows) { if (isWindows) {
tray.on("click", () => { tray.on("click", () => {
windowManager applicationWindow.show()
.ensureMainWindow()
.catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to open lens`, { error })); .catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to open lens`, { error }));
}); });
} }
@ -54,7 +53,7 @@ export function initTray(
return disposer( return disposer(
autorun(() => { autorun(() => {
try { try {
const menu = createTrayMenu(windowManager, toJS(trayMenuItems.get()), navigateToPreferences, stopServicesAndExitApp, isAutoUpdateEnabled); const menu = createTrayMenu(toJS(trayMenuItems.get()), navigateToPreferences, stopServicesAndExitApp, isAutoUpdateEnabled, applicationWindow, showAbout);
tray.setContextMenu(menu); tray.setContextMenu(menu);
} catch (error) { } catch (error) {
@ -79,19 +78,18 @@ function getMenuItemConstructorOptions(trayItem: TrayMenuRegistration): Electron
} }
function createTrayMenu( function createTrayMenu(
windowManager: WindowManager,
extensionTrayItems: TrayMenuRegistration[], extensionTrayItems: TrayMenuRegistration[],
navigateToPreferences: () => void, navigateToPreferences: () => void,
stopServicesAndExitApp: () => void, stopServicesAndExitApp: () => void,
isAutoUpdateEnabled: () => boolean, isAutoUpdateEnabled: () => boolean,
applicationWindow: LensWindow,
showAbout: () => void,
): Menu { ): Menu {
let template: Electron.MenuItemConstructorOptions[] = [ let template: Electron.MenuItemConstructorOptions[] = [
{ {
label: `Open ${productName}`, label: `Open ${productName}`,
click() { click() {
windowManager applicationWindow.show().catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to open lens`, { error }));
.ensureMainWindow()
.catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to open lens`, { error }));
}, },
}, },
{ {
@ -107,7 +105,7 @@ function createTrayMenu(
label: "Check for updates", label: "Check for updates",
click() { click() {
checkForUpdates() checkForUpdates()
.then(() => windowManager.ensureMainWindow()); .then(() => applicationWindow.show());
}, },
}); });
} }
@ -118,7 +116,7 @@ function createTrayMenu(
{ {
label: `About ${productName}`, label: `About ${productName}`,
click() { click() {
windowManager.ensureMainWindow() applicationWindow.show()
.then(showAbout) .then(showAbout)
.catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to show Lens About view`, { error })); .catch(error => logger.error(`${TRAY_LOG_PREFIX}: Failed to show Lens About view`, { error }));
}, },

View File

@ -1,20 +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 { WindowManager } from "./window-manager";
import lensProxyPortNumberStateInjectable from "./lens-proxy-port-number-state.injectable";
const windowManagerInjectable = getInjectable({
id: "window-manager",
instantiate: (di) =>
new WindowManager({
lensProxyPortNumberState: di.inject(lensProxyPortNumberStateInjectable),
}),
causesSideEffects: true,
});
export default windowManagerInjectable;

View File

@ -1,289 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { ClusterId } from "../common/cluster-types";
import { makeObservable, observable } from "mobx";
import { app, BrowserWindow, dialog, ipcMain, webContents } from "electron";
import windowStateKeeper from "electron-window-state";
import { appEventBus } from "../common/app-event-bus/event-bus";
import { ipcMainOn } from "../common/ipc";
import { delay, iter, openBrowser } from "../common/utils";
import type { ClusterFrameInfo } from "../common/cluster-frames";
import { clusterFrameMap } from "../common/cluster-frames";
import { IpcRendererNavigationEvents } from "../renderer/navigation/events";
import logger from "./logger";
import { isMac, productName } from "../common/vars";
import { bundledExtensionsLoaded } from "../common/ipc/extension-handling";
function isHideable(window: BrowserWindow | null): boolean {
return Boolean(window && !window.isDestroyed());
}
export interface SendToViewArgs {
channel: string;
frameInfo?: ClusterFrameInfo;
data?: any[];
}
interface Dependencies {
lensProxyPortNumberState: { get: () => number };
}
export class WindowManager {
protected mainWindow: BrowserWindow;
protected splashWindow: BrowserWindow;
protected windowState: windowStateKeeper.State;
protected disposers: Record<string, Function> = {};
@observable activeClusterId: ClusterId;
constructor(private dependencies: Dependencies) {
makeObservable(this);
this.bindEvents();
}
get mainContentUrl() {
return `http://localhost:${this.dependencies.lensProxyPortNumberState.get()}`;
}
private async initMainWindow(showSplash: boolean) {
// Manage main window size and position with state persistence
if (!this.windowState) {
this.windowState = windowStateKeeper({
defaultHeight: 900,
defaultWidth: 1440,
});
}
if (!this.mainWindow) {
// show icon in dock (mac-os only)
app.dock?.show();
const { width, height, x, y } = this.windowState;
this.mainWindow = new BrowserWindow({
x, y, width, height,
title: productName,
show: false,
minWidth: 700, // accommodate 800 x 600 display minimum
minHeight: 500, // accommodate 800 x 600 display minimum
titleBarStyle: isMac ? "hiddenInset" : "hidden",
frame: isMac,
backgroundColor: "#1e2124",
webPreferences: {
nodeIntegration: true,
nodeIntegrationInSubFrames: true,
webviewTag: true,
contextIsolation: false,
nativeWindowOpen: false,
},
});
this.windowState.manage(this.mainWindow);
// open external links in default browser (target=_blank, window.open)
this.mainWindow
.on("focus", () => {
appEventBus.emit({ name: "app", action: "focus" });
})
.on("blur", () => {
appEventBus.emit({ name: "app", action: "blur" });
})
.on("closed", () => {
// clean up
this.windowState.unmanage();
this.mainWindow = null;
this.splashWindow = null;
app.dock?.hide(); // hide icon in dock (mac-os)
})
.webContents
.on("dom-ready", () => {
appEventBus.emit({ name: "app", action: "dom-ready" });
})
.on("did-fail-load", (_event, code, desc) => {
logger.error(`[WINDOW-MANAGER]: Failed to load Main window`, { code, desc });
})
.on("did-finish-load", () => {
logger.info("[WINDOW-MANAGER]: Main window loaded");
})
.on("will-attach-webview", (event, webPreferences, params) => {
logger.debug("[WINDOW-MANAGER]: Attaching webview");
// Following is security recommendations because we allow webview tag (webviewTag: true)
// suggested by https://www.electronjs.org/docs/tutorial/security#11-verify-webview-options-before-creation
// and https://www.electronjs.org/docs/tutorial/security#10-do-not-use-allowpopups
if (webPreferences.preload) {
logger.warn("[WINDOW-MANAGER]: Strip away preload scripts of webview");
delete webPreferences.preload;
}
// @ts-expect-error some electron version uses webPreferences.preloadURL/webPreferences.preload
if (webPreferences.preloadURL) {
logger.warn("[WINDOW-MANAGER]: Strip away preload scripts of webview");
delete webPreferences.preload;
}
if (params.allowpopups) {
logger.warn("[WINDOW-MANAGER]: We do not allow allowpopups props, stop webview from renderer");
// event.preventDefault() will destroy the guest page.
event.preventDefault();
return;
}
// Always disable Node.js integration for all webviews
webPreferences.nodeIntegration = false;
})
.setWindowOpenHandler((details) => {
openBrowser(details.url).catch(error => {
logger.error("[WINDOW-MANAGER]: failed to open browser", { error });
});
return { action: "deny" };
});
}
try {
if (showSplash) await this.showSplash();
logger.info(`[WINDOW-MANAGER]: Loading Main window from url: ${this.mainContentUrl} ...`);
await this.mainWindow.loadURL(this.mainContentUrl);
} catch (error) {
logger.error("Loading main window failed", { error });
dialog.showErrorBox("ERROR!", error.toString());
}
}
protected bindEvents() {
// track visible cluster from ui
ipcMainOn(IpcRendererNavigationEvents.CLUSTER_VIEW_CURRENT_ID, (event, clusterId: ClusterId) => {
this.activeClusterId = clusterId;
});
}
async ensureMainWindow(showSplash = true): Promise<BrowserWindow> {
// This needs to be ready to hear the IPC message before the window is loaded
let viewHasLoaded = Promise.resolve();
if (!this.mainWindow) {
viewHasLoaded = new Promise<void>(resolve => {
ipcMain.once(bundledExtensionsLoaded, () => resolve());
});
await this.initMainWindow(showSplash);
}
try {
await viewHasLoaded;
await delay(50); // wait just a bit longer to let the first round of rendering happen
logger.info("[WINDOW-MANAGER]: Main window has reported that it has loaded");
this.mainWindow.show();
this.splashWindow?.close();
this.splashWindow = undefined;
setTimeout(() => {
appEventBus.emit({ name: "app", action: "start" });
}, 1000);
} catch (error) {
logger.error(`Showing main window failed: ${error.stack || error}`);
dialog.showErrorBox("ERROR!", error.toString());
}
return this.mainWindow;
}
sendToView({ channel, frameInfo, data = [] }: SendToViewArgs) {
if (frameInfo) {
this.mainWindow.webContents.sendToFrame([frameInfo.processId, frameInfo.frameId], channel, ...data);
} else {
this.mainWindow.webContents.send(channel, ...data);
}
}
async navigateExtension(extId: string, pageId?: string, params?: Record<string, any>, frameId?: number) {
await this.ensureMainWindow();
const frameInfo = iter.find(clusterFrameMap.values(), frameInfo => frameInfo.frameId === frameId);
this.sendToView({
channel: "extension:navigate",
frameInfo,
data: [extId, pageId, params],
});
}
async navigate(url: string, frameId?: number) {
await this.ensureMainWindow();
this.navigateSync(url, frameId);
}
navigateSync(url: string, frameId?: number) {
const frameInfo = iter.find(clusterFrameMap.values(), frameInfo => frameInfo.frameId === frameId);
const channel = frameInfo
? IpcRendererNavigationEvents.NAVIGATE_IN_CLUSTER
: IpcRendererNavigationEvents.NAVIGATE_IN_APP;
this.sendToView({
channel,
frameInfo,
data: [url],
});
}
reload() {
const frameInfo = clusterFrameMap.get(this.activeClusterId);
if (frameInfo) {
this.sendToView({ channel: IpcRendererNavigationEvents.RELOAD_PAGE, frameInfo });
} else {
webContents.getAllWebContents().filter(wc => wc.getType() === "window").forEach(wc => {
wc.reload();
wc.clearHistory();
});
}
}
async showSplash() {
if (!this.splashWindow) {
this.splashWindow = new BrowserWindow({
width: 500,
height: 300,
backgroundColor: "#1e2124",
center: true,
frame: false,
resizable: false,
show: false,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
nodeIntegrationInSubFrames: true,
nativeWindowOpen: true,
},
});
await this.splashWindow.loadURL("static://splash.html");
}
this.splashWindow.show();
}
hide() {
if (isHideable(this.mainWindow)) {
this.mainWindow.hide();
}
if (isHideable(this.splashWindow)) {
this.splashWindow.hide();
}
}
destroy() {
this.mainWindow.destroy();
this.splashWindow.destroy();
this.mainWindow = null;
this.splashWindow = null;
Object.entries(this.disposers).forEach(([name, dispose]) => {
dispose();
delete this.disposers[name];
});
}
}

View File

@ -36,6 +36,7 @@ import hotbarStoreInjectable from "../common/hotbar-store.injectable";
import themeStoreInjectable from "./theme-store.injectable"; import themeStoreInjectable from "./theme-store.injectable";
import apiManagerInjectable from "./components/kube-object-menu/dependencies/api-manager.injectable"; import apiManagerInjectable from "./components/kube-object-menu/dependencies/api-manager.injectable";
import { ApiManager } from "../common/k8s-api/api-manager"; import { ApiManager } from "../common/k8s-api/api-manager";
import lensResourcesDirInjectable from "../common/vars/lens-resources-dir.injectable";
export const getDiForUnitTesting = ( export const getDiForUnitTesting = (
{ doGeneralOverrides } = { doGeneralOverrides: false }, { doGeneralOverrides } = { doGeneralOverrides: false },
@ -63,6 +64,8 @@ export const getDiForUnitTesting = (
di.override(getAbsolutePathInjectable, () => getAbsolutePathFake); di.override(getAbsolutePathInjectable, () => getAbsolutePathFake);
di.override(joinPathsInjectable, () => joinPathsFake); di.override(joinPathsInjectable, () => joinPathsFake);
di.override(lensResourcesDirInjectable, () => "/irrelevant");
// eslint-disable-next-line unused-imports/no-unused-vars-ts // eslint-disable-next-line unused-imports/no-unused-vars-ts
di.override(extensionsStoreInjectable, () => ({ isEnabled: ({ id, isBundled }) => false }) as ExtensionsStore); di.override(extensionsStoreInjectable, () => ({ isEnabled: ({ id, isBundled }) => false }) as ExtensionsStore);

View File

@ -8,9 +8,9 @@ import getValueFromRegisteredChannelInjectable from "../renderer/app-paths/get-v
import registerChannelInjectable from "../main/app-paths/register-channel/register-channel.injectable"; import registerChannelInjectable from "../main/app-paths/register-channel/register-channel.injectable";
import asyncFn from "@async-fn/jest"; import asyncFn from "@async-fn/jest";
import registerIpcChannelListenerInjectable from "../renderer/app-paths/get-value-from-registered-channel/register-ipc-channel-listener.injectable"; import registerIpcChannelListenerInjectable from "../renderer/app-paths/get-value-from-registered-channel/register-ipc-channel-listener.injectable";
import windowManagerInjectable from "../main/window-manager.injectable"; import type { SendToViewArgs } from "../main/start-main-application/lens-window/application-window/lens-window-injection-token";
import type { SendToViewArgs, WindowManager } from "../main/window-manager"; import sendToChannelInElectronBrowserWindowInjectable from "../main/start-main-application/lens-window/application-window/send-to-channel-in-electron-browser-window.injectable";
import { appNavigationIpcChannel } from "../common/front-end-routing/navigation-ipc-channel";
export const overrideIpcBridge = ({ export const overrideIpcBridge = ({
rendererDi, rendererDi,
@ -68,7 +68,10 @@ export const overrideIpcBridge = ({
mainIpcRegistrations.set(channel, callback); mainIpcRegistrations.set(channel, callback);
}); });
const rendererIpcFakeHandles = new Map<string, ((...args: any[]) => void)[]>(); const rendererIpcFakeHandles = new Map<
string,
((...args: any[]) => void)[]
>();
rendererDi.override( rendererDi.override(
registerIpcChannelListenerInjectable, registerIpcChannelListenerInjectable,
@ -81,21 +84,14 @@ export const overrideIpcBridge = ({
); );
mainDi.override( mainDi.override(
windowManagerInjectable, sendToChannelInElectronBrowserWindowInjectable,
() => () =>
({ (browserWindow, { channel: channelName, data }: SendToViewArgs) => {
ensureMainWindow: () => Promise.resolve(), const handles = rendererIpcFakeHandles.get(channelName);
sendToView: ({ channel: channelName, data }: SendToViewArgs) => { handles.forEach((handle) => handle(...data));
const handles = rendererIpcFakeHandles.get(channelName);
handles.forEach(handle => handle(...data)); return Promise.resolve();
}, },
navigateSync(url: string) {
this.sendToView({ channel: appNavigationIpcChannel.name, data: [url] });
},
} as unknown as WindowManager),
); );
}; };