From 0cad3e7baec38996a04d51b01eedb6f0423234ee Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Mon, 5 Dec 2022 10:19:24 -0500 Subject: [PATCH] Move initializing CatalogCategories to runnable in bootstrap Signed-off-by: Sebastian Malton --- src/common/ipc/dialog.ts | 6 -- .../path-picking-dialog/common/channel.ts | 18 ++++ .../main/handle-pick-paths.injectable.ts | 15 ++++ .../renderer/pick-paths.injectable.ts | 33 ++++++++ ...n-dialog.global-override-for-injectable.ts | 9 ++ .../features/show-open-dialog.injectable.ts | 17 ++++ .../setup-ipc-main-handlers.injectable.ts | 3 - .../setup-ipc-main-handlers.ts | 6 -- .../ipc/ask-user-for-file-paths.injectable.ts | 25 ++++-- .../before-frame-starts-injection-token.ts | 10 --- ...tes-cluster-catalog-add-menu.injectable.ts | 82 +++++++++++++++++++ src/renderer/bootstrap.tsx | 14 ---- .../components/path-picker/path-picker.tsx | 72 ++++++++-------- .../catalog-category-registry.tsx | 79 ------------------ src/renderer/initializers/index.ts | 1 - src/renderer/ipc/index.ts | 6 -- 16 files changed, 225 insertions(+), 171 deletions(-) delete mode 100644 src/common/ipc/dialog.ts create mode 100644 src/features/path-picking-dialog/common/channel.ts create mode 100644 src/features/path-picking-dialog/main/handle-pick-paths.injectable.ts create mode 100644 src/features/path-picking-dialog/renderer/pick-paths.injectable.ts create mode 100644 src/main/electron-app/features/show-open-dialog.global-override-for-injectable.ts create mode 100644 src/main/electron-app/features/show-open-dialog.injectable.ts delete mode 100644 src/renderer/before-frame-starts/before-frame-starts-injection-token.ts create mode 100644 src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-catalog-add-menu.injectable.ts delete mode 100644 src/renderer/initializers/catalog-category-registry.tsx diff --git a/src/common/ipc/dialog.ts b/src/common/ipc/dialog.ts deleted file mode 100644 index eab621a280..0000000000 --- a/src/common/ipc/dialog.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export const openFilePickingDialogChannel = "dialog:open:file-picking"; diff --git a/src/features/path-picking-dialog/common/channel.ts b/src/features/path-picking-dialog/common/channel.ts new file mode 100644 index 0000000000..98c19b4ad6 --- /dev/null +++ b/src/features/path-picking-dialog/common/channel.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { OpenDialogOptions } from "electron"; +import type { RequestChannel } from "../../../common/utils/channel/request-channel-listener-injection-token"; + +export type PathPickingResponse = { + canceled: true; +} | { + canceled: false; + paths: string[]; +}; + +export const openPathPickingDialogChannel: RequestChannel = { + id: "open-path-picking-dialog", +}; diff --git a/src/features/path-picking-dialog/main/handle-pick-paths.injectable.ts b/src/features/path-picking-dialog/main/handle-pick-paths.injectable.ts new file mode 100644 index 0000000000..d566ac9728 --- /dev/null +++ b/src/features/path-picking-dialog/main/handle-pick-paths.injectable.ts @@ -0,0 +1,15 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import askUserForFilePathsInjectable from "../../../main/ipc/ask-user-for-file-paths.injectable"; +import { getRequestChannelListenerInjectable } from "../../../main/utils/channel/channel-listeners/listener-tokens"; +import { openPathPickingDialogChannel } from "../common/channel"; + +const openPathPickingDialogListener = getRequestChannelListenerInjectable({ + channel: openPathPickingDialogChannel, + handler: (di) => di.inject(askUserForFilePathsInjectable), +}); + +export default openPathPickingDialogListener; diff --git a/src/features/path-picking-dialog/renderer/pick-paths.injectable.ts b/src/features/path-picking-dialog/renderer/pick-paths.injectable.ts new file mode 100644 index 0000000000..d4e8b25545 --- /dev/null +++ b/src/features/path-picking-dialog/renderer/pick-paths.injectable.ts @@ -0,0 +1,33 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import type { PathPickOpts } from "../../../renderer/components/path-picker"; +import requestFromChannelInjectable from "../../../renderer/utils/channel/request-from-channel.injectable"; +import { openPathPickingDialogChannel } from "../common/channel"; + +export type OpenPathPickingDialog = (options: PathPickOpts) => Promise; + +const openPathPickingDialogInjectable = getInjectable({ + id: "open-path-picking-dialog", + instantiate: (di): OpenPathPickingDialog => { + const requestFromChannel = di.inject(requestFromChannelInjectable); + + return async (options) => { + const { onPick, onCancel, label, ...dialogOptions } = options; + const response = await requestFromChannel(openPathPickingDialogChannel, { + message: label, + ...dialogOptions, + }); + + if (response.canceled) { + await onCancel?.(); + } else { + await onPick?.(response.paths); + } + }; + }, +}); + +export default openPathPickingDialogInjectable; diff --git a/src/main/electron-app/features/show-open-dialog.global-override-for-injectable.ts b/src/main/electron-app/features/show-open-dialog.global-override-for-injectable.ts new file mode 100644 index 0000000000..94e9afbeb4 --- /dev/null +++ b/src/main/electron-app/features/show-open-dialog.global-override-for-injectable.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getGlobalOverrideForFunction } from "../../../common/test-utils/get-global-override-for-function"; +import showOpenDialogInjectable from "./show-open-dialog.injectable"; + +export default getGlobalOverrideForFunction(showOpenDialogInjectable); diff --git a/src/main/electron-app/features/show-open-dialog.injectable.ts b/src/main/electron-app/features/show-open-dialog.injectable.ts new file mode 100644 index 0000000000..d2b15e721d --- /dev/null +++ b/src/main/electron-app/features/show-open-dialog.injectable.ts @@ -0,0 +1,17 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import type { OpenDialogOptions } from "electron"; +import { dialog } from "electron"; + +export type ShowOpenDialog = (options: OpenDialogOptions) => Promise; + +const showOpenDialogInjectable = getInjectable({ + id: "show-open-dialog", + instantiate: (): ShowOpenDialog => opts => dialog.showOpenDialog(opts), + causesSideEffects: true, +}); + +export default showOpenDialogInjectable; diff --git a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts index 5c701374ce..99bb94e423 100644 --- a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts +++ b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts @@ -9,7 +9,6 @@ import clusterStoreInjectable from "../../../../common/cluster-store/cluster-sto import { onLoadOfApplicationInjectionToken } from "../../../start-main-application/runnable-tokens/on-load-of-application-injection-token"; import operatingSystemThemeInjectable from "../../../theme/operating-system-theme.injectable"; import catalogEntityRegistryInjectable from "../../../catalog/entity-registry.injectable"; -import askUserForFilePathsInjectable from "../../../ipc/ask-user-for-file-paths.injectable"; import applicationMenuItemCompositeInjectable from "../../../../features/application-menu/main/application-menu-item-composite.injectable"; import emitAppEventInjectable from "../../../../common/app-event-bus/emit-event.injectable"; import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable"; @@ -23,7 +22,6 @@ const setupIpcMainHandlersInjectable = getInjectable({ const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); const clusterStore = di.inject(clusterStoreInjectable); const operatingSystemTheme = di.inject(operatingSystemThemeInjectable); - const askUserForFilePaths = di.inject(askUserForFilePathsInjectable); const emitAppEvent = di.inject(emitAppEventInjectable); const getClusterById = di.inject(getClusterByIdInjectable); @@ -37,7 +35,6 @@ const setupIpcMainHandlersInjectable = getInjectable({ catalogEntityRegistry, clusterStore, operatingSystemTheme, - askUserForFilePaths, emitAppEvent, getClusterById, }); diff --git a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts index 5088fe7804..15daf73383 100644 --- a/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts +++ b/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts @@ -14,10 +14,8 @@ import { pushCatalogToRenderer } from "../../../catalog-pusher"; import type { IComputedValue } from "mobx"; import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel } from "../../../../common/ipc/window"; import { handleWindowAction, onLocationChange } from "../../../ipc/window"; -import { openFilePickingDialogChannel } from "../../../../common/ipc/dialog"; import { getNativeThemeChannel } from "../../../../common/ipc/native-theme"; import type { Theme } from "../../../theme/operating-system-theme-state.injectable"; -import type { AskUserForFilePaths } from "../../../ipc/ask-user-for-file-paths.injectable"; import type { ApplicationMenuItemTypes } from "../../../../features/application-menu/main/menu-items/application-menu-item-injection-token"; import type { Composite } from "../../../../common/utils/composite/get-composite/get-composite"; import { getApplicationMenuTemplate } from "../../../../features/application-menu/main/populate-application-menu.injectable"; @@ -29,7 +27,6 @@ interface Dependencies { catalogEntityRegistry: CatalogEntityRegistry; clusterStore: ClusterStore; operatingSystemTheme: IComputedValue; - askUserForFilePaths: AskUserForFilePaths; emitAppEvent: EmitAppEvent; getClusterById: GetClusterById; } @@ -39,7 +36,6 @@ export const setupIpcMainHandlers = ({ catalogEntityRegistry, clusterStore, operatingSystemTheme, - askUserForFilePaths, emitAppEvent, getClusterById, }: Dependencies) => { @@ -73,8 +69,6 @@ export const setupIpcMainHandlers = ({ ipcMainOn(windowLocationChangedChannel, () => onLocationChange()); - ipcMainHandle(openFilePickingDialogChannel, (event, opts) => askUserForFilePaths(opts)); - ipcMainHandle(broadcastMainChannel, (event, channel, ...args) => broadcastMessage(channel, ...args)); ipcMainOn(windowOpenAppMenuAsContextMenuChannel, async (event) => { diff --git a/src/main/ipc/ask-user-for-file-paths.injectable.ts b/src/main/ipc/ask-user-for-file-paths.injectable.ts index 9fc1ac1471..f8a0685d7c 100644 --- a/src/main/ipc/ask-user-for-file-paths.injectable.ts +++ b/src/main/ipc/ask-user-for-file-paths.injectable.ts @@ -3,34 +3,41 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { OpenDialogOptions } from "electron"; -import { dialog } from "electron"; import { getInjectable } from "@ogre-tools/injectable"; import showApplicationWindowInjectable from "../start-main-application/lens-window/show-application-window.injectable"; +import type { RequestChannelHandler } from "../utils/channel/channel-listeners/listener-tokens"; +import type { openPathPickingDialogChannel } from "../../features/path-picking-dialog/common/channel"; +import showOpenDialogInjectable from "../electron-app/features/show-open-dialog.injectable"; // TODO: Replace leaking electron with abstraction -export type AskUserForFilePaths = ( - dialogOptions: OpenDialogOptions -) => Promise<{ canceled: boolean; filePaths: string[] }>; +export type AskUserForFilePaths = RequestChannelHandler; const askUserForFilePathsInjectable = getInjectable({ id: "ask-user-for-file-paths", instantiate: (di): AskUserForFilePaths => { const showApplicationWindow = di.inject(showApplicationWindowInjectable); + const showOpenDialog = di.inject(showOpenDialogInjectable); return async (dialogOptions) => { await showApplicationWindow(); - const { canceled, filePaths } = await dialog.showOpenDialog( + const { canceled, filePaths } = await showOpenDialog( dialogOptions, ); - return { canceled, filePaths }; + if (canceled) { + return { + canceled, + }; + } + + return { + canceled: false, + paths: filePaths, + }; }; }, - - causesSideEffects: true, }); export default askUserForFilePathsInjectable; diff --git a/src/renderer/before-frame-starts/before-frame-starts-injection-token.ts b/src/renderer/before-frame-starts/before-frame-starts-injection-token.ts deleted file mode 100644 index e494508329..0000000000 --- a/src/renderer/before-frame-starts/before-frame-starts-injection-token.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * 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 "../../common/runnable/run-many-for"; - -export const beforeFrameStartsInjectionToken = getInjectionToken({ - id: "before-frame-starts", -}); diff --git a/src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-catalog-add-menu.injectable.ts b/src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-catalog-add-menu.injectable.ts new file mode 100644 index 0000000000..cb6e9688cc --- /dev/null +++ b/src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-catalog-add-menu.injectable.ts @@ -0,0 +1,82 @@ +/** + * 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 kubernetesClusterCategoryInjectable from "../../../common/catalog/categories/kubernetes-cluster.injectable"; +import navigateToAddClusterInjectable from "../../../common/front-end-routing/routes/add-cluster/navigate-to-add-cluster.injectable"; +import isLinuxInjectable from "../../../common/vars/is-linux.injectable"; +import isWindowsInjectable from "../../../common/vars/is-windows.injectable"; +import openPathPickingDialogInjectable from "../../../features/path-picking-dialog/renderer/pick-paths.injectable"; +import addSyncEntriesInjectable from "../../initializers/add-sync-entries.injectable"; +import { beforeFrameStartsInjectionToken } from "../tokens"; + +const setupKubernetesClusterCatalogAddMenuListenerInjectable = getInjectable({ + id: "setup-kubernetes-cluster-catalog-add-menu-listener", + instantiate: (di) => { + const navigateToAddCluster = di.inject(navigateToAddClusterInjectable); + const addSyncEntries = di.inject(addSyncEntriesInjectable); + const kubernetesClusterCategory = di.inject(kubernetesClusterCategoryInjectable); + const isWindows = di.inject(isWindowsInjectable); + const isLinux = di.inject(isLinuxInjectable); + const openPathPickingDialog = di.inject(openPathPickingDialogInjectable); + + return { + id: "setup-kubernetes-cluster-catalog-add-menu-listener", + run: () => { + kubernetesClusterCategory.on("catalogAddMenu", ctx => { + ctx.menuItems.push( + { + icon: "text_snippet", + title: "Add from kubeconfig", + onClick: navigateToAddCluster, + }, + ); + + if (isWindows || isLinux) { + ctx.menuItems.push( + { + icon: "create_new_folder", + title: "Sync kubeconfig folder(s)", + defaultAction: true, + onClick: () => openPathPickingDialog({ + label: "Sync folder(s)", + buttonLabel: "Sync", + properties: ["showHiddenFiles", "multiSelections", "openDirectory"], + onPick: addSyncEntries, + }), + }, + { + icon: "note_add", + title: "Sync kubeconfig file(s)", + onClick: () => openPathPickingDialog({ + label: "Sync file(s)", + buttonLabel: "Sync", + properties: ["showHiddenFiles", "multiSelections", "openFile"], + onPick: addSyncEntries, + }), + }, + ); + } else { + ctx.menuItems.push( + { + icon: "create_new_folder", + title: "Sync kubeconfig(s)", + defaultAction: true, + onClick: () => openPathPickingDialog({ + label: "Sync file(s)", + buttonLabel: "Sync", + properties: ["showHiddenFiles", "multiSelections", "openFile", "openDirectory"], + onPick: addSyncEntries, + }), + }, + ); + } + }); + }, + }; + }, + injectionToken: beforeFrameStartsInjectionToken, +}); + +export default setupKubernetesClusterCatalogAddMenuListenerInjectable; diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index fa1b8f224d..7962a37e1c 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -28,16 +28,11 @@ import commandOverlayInjectable from "./components/command-palette/command-overl import { Router } from "react-router"; import historyInjectable from "./navigation/history.injectable"; import themeStoreInjectable from "./themes/store.injectable"; -import navigateToAddClusterInjectable from "../common/front-end-routing/routes/add-cluster/navigate-to-add-cluster.injectable"; -import addSyncEntriesInjectable from "./initializers/add-sync-entries.injectable"; import hotbarStoreInjectable from "../common/hotbars/store.injectable"; import openDeleteClusterDialogInjectable from "./components/delete-cluster-dialog/open.injectable"; -import kubernetesClusterCategoryInjectable from "../common/catalog/categories/kubernetes-cluster.injectable"; import assert from "assert"; import startFrameInjectable from "./start-frame/start-frame.injectable"; import loggerInjectable from "../common/logger.injectable"; -import isLinuxInjectable from "../common/vars/is-linux.injectable"; -import isWindowsInjectable from "../common/vars/is-windows.injectable"; export async function bootstrap(di: DiContainer) { const startFrame = di.inject(startFrameInjectable); @@ -50,15 +45,6 @@ export async function bootstrap(di: DiContainer) { assert(rootElem, "#app MUST exist"); - logger.info(`${logPrefix} initializing CatalogCategoryRegistryEntries`); - initializers.initCatalogCategoryRegistryEntries({ - navigateToAddCluster: di.inject(navigateToAddClusterInjectable), - addSyncEntries: di.inject(addSyncEntriesInjectable), - kubernetesClusterCategory: di.inject(kubernetesClusterCategoryInjectable), - isLinux: di.inject(isLinuxInjectable), - isWindows: di.inject(isWindowsInjectable), - }); - logger.info(`${logPrefix} initializing Catalog`); initializers.initCatalog({ openCommandDialog: di.inject(commandOverlayInjectable).open, diff --git a/src/renderer/components/path-picker/path-picker.tsx b/src/renderer/components/path-picker/path-picker.tsx index 416bd3fd42..8fabb37626 100644 --- a/src/renderer/components/path-picker/path-picker.tsx +++ b/src/renderer/components/path-picker/path-picker.tsx @@ -3,12 +3,14 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import { withInjectables } from "@ogre-tools/injectable-react"; import type { FileFilter, OpenDialogOptions } from "electron"; import { observer } from "mobx-react"; import React from "react"; +import type { OpenPathPickingDialog } from "../../../features/path-picking-dialog/renderer/pick-paths.injectable"; +import openPathPickingDialogInjectable from "../../../features/path-picking-dialog/renderer/pick-paths.injectable"; import { cssNames } from "../../utils"; import { Button } from "../button"; -import { requestOpenFilePickingDialog } from "../../ipc"; export interface PathPickOpts { label: string; @@ -26,40 +28,36 @@ export interface PathPickerProps extends PathPickOpts { disabled?: boolean; } -@observer -export class PathPicker extends React.Component { - static async pick(opts: PathPickOpts) { - const { onPick, onCancel, label, ...dialogOptions } = opts; - - const { canceled, filePaths } = await requestOpenFilePickingDialog({ - message: label, - ...dialogOptions, - }); - - if (canceled) { - await onCancel?.(); - } else { - await onPick?.(filePaths); - } - } - - async onClick() { - const { className, disabled, ...pickOpts } = this.props; - - return PathPicker.pick(pickOpts); - } - - render() { - const { className, label, disabled } = this.props; - - return ( -