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

Move initializing CatalogCategories to runnable in bootstrap

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2022-12-05 10:19:24 -05:00
parent 9e1d182459
commit 0cad3e7bae
16 changed files with 225 additions and 171 deletions

View File

@ -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";

View File

@ -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<OpenDialogOptions, PathPickingResponse> = {
id: "open-path-picking-dialog",
};

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 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;

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 } 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<void>;
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;

View File

@ -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);

View File

@ -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<Electron.OpenDialogReturnValue>;
const showOpenDialogInjectable = getInjectable({
id: "show-open-dialog",
instantiate: (): ShowOpenDialog => opts => dialog.showOpenDialog(opts),
causesSideEffects: true,
});
export default showOpenDialogInjectable;

View File

@ -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 { onLoadOfApplicationInjectionToken } from "../../../start-main-application/runnable-tokens/on-load-of-application-injection-token";
import operatingSystemThemeInjectable from "../../../theme/operating-system-theme.injectable"; import operatingSystemThemeInjectable from "../../../theme/operating-system-theme.injectable";
import catalogEntityRegistryInjectable from "../../../catalog/entity-registry.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 applicationMenuItemCompositeInjectable from "../../../../features/application-menu/main/application-menu-item-composite.injectable";
import emitAppEventInjectable from "../../../../common/app-event-bus/emit-event.injectable"; import emitAppEventInjectable from "../../../../common/app-event-bus/emit-event.injectable";
import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable"; import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable";
@ -23,7 +22,6 @@ const setupIpcMainHandlersInjectable = getInjectable({
const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable);
const clusterStore = di.inject(clusterStoreInjectable); const clusterStore = di.inject(clusterStoreInjectable);
const operatingSystemTheme = di.inject(operatingSystemThemeInjectable); const operatingSystemTheme = di.inject(operatingSystemThemeInjectable);
const askUserForFilePaths = di.inject(askUserForFilePathsInjectable);
const emitAppEvent = di.inject(emitAppEventInjectable); const emitAppEvent = di.inject(emitAppEventInjectable);
const getClusterById = di.inject(getClusterByIdInjectable); const getClusterById = di.inject(getClusterByIdInjectable);
@ -37,7 +35,6 @@ const setupIpcMainHandlersInjectable = getInjectable({
catalogEntityRegistry, catalogEntityRegistry,
clusterStore, clusterStore,
operatingSystemTheme, operatingSystemTheme,
askUserForFilePaths,
emitAppEvent, emitAppEvent,
getClusterById, getClusterById,
}); });

View File

@ -14,10 +14,8 @@ import { pushCatalogToRenderer } from "../../../catalog-pusher";
import type { IComputedValue } from "mobx"; import type { IComputedValue } from "mobx";
import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel } from "../../../../common/ipc/window"; import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel } from "../../../../common/ipc/window";
import { handleWindowAction, onLocationChange } from "../../../ipc/window"; import { handleWindowAction, onLocationChange } from "../../../ipc/window";
import { openFilePickingDialogChannel } from "../../../../common/ipc/dialog";
import { getNativeThemeChannel } from "../../../../common/ipc/native-theme"; import { getNativeThemeChannel } from "../../../../common/ipc/native-theme";
import type { Theme } from "../../../theme/operating-system-theme-state.injectable"; 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 { 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 type { Composite } from "../../../../common/utils/composite/get-composite/get-composite";
import { getApplicationMenuTemplate } from "../../../../features/application-menu/main/populate-application-menu.injectable"; import { getApplicationMenuTemplate } from "../../../../features/application-menu/main/populate-application-menu.injectable";
@ -29,7 +27,6 @@ interface Dependencies {
catalogEntityRegistry: CatalogEntityRegistry; catalogEntityRegistry: CatalogEntityRegistry;
clusterStore: ClusterStore; clusterStore: ClusterStore;
operatingSystemTheme: IComputedValue<Theme>; operatingSystemTheme: IComputedValue<Theme>;
askUserForFilePaths: AskUserForFilePaths;
emitAppEvent: EmitAppEvent; emitAppEvent: EmitAppEvent;
getClusterById: GetClusterById; getClusterById: GetClusterById;
} }
@ -39,7 +36,6 @@ export const setupIpcMainHandlers = ({
catalogEntityRegistry, catalogEntityRegistry,
clusterStore, clusterStore,
operatingSystemTheme, operatingSystemTheme,
askUserForFilePaths,
emitAppEvent, emitAppEvent,
getClusterById, getClusterById,
}: Dependencies) => { }: Dependencies) => {
@ -73,8 +69,6 @@ export const setupIpcMainHandlers = ({
ipcMainOn(windowLocationChangedChannel, () => onLocationChange()); ipcMainOn(windowLocationChangedChannel, () => onLocationChange());
ipcMainHandle(openFilePickingDialogChannel, (event, opts) => askUserForFilePaths(opts));
ipcMainHandle(broadcastMainChannel, (event, channel, ...args) => broadcastMessage(channel, ...args)); ipcMainHandle(broadcastMainChannel, (event, channel, ...args) => broadcastMessage(channel, ...args));
ipcMainOn(windowOpenAppMenuAsContextMenuChannel, async (event) => { ipcMainOn(windowOpenAppMenuAsContextMenuChannel, async (event) => {

View File

@ -3,34 +3,41 @@
* 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 { OpenDialogOptions } from "electron";
import { dialog } from "electron";
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import showApplicationWindowInjectable from "../start-main-application/lens-window/show-application-window.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 // TODO: Replace leaking electron with abstraction
export type AskUserForFilePaths = ( export type AskUserForFilePaths = RequestChannelHandler<typeof openPathPickingDialogChannel>;
dialogOptions: OpenDialogOptions
) => Promise<{ canceled: boolean; filePaths: string[] }>;
const askUserForFilePathsInjectable = getInjectable({ const askUserForFilePathsInjectable = getInjectable({
id: "ask-user-for-file-paths", id: "ask-user-for-file-paths",
instantiate: (di): AskUserForFilePaths => { instantiate: (di): AskUserForFilePaths => {
const showApplicationWindow = di.inject(showApplicationWindowInjectable); const showApplicationWindow = di.inject(showApplicationWindowInjectable);
const showOpenDialog = di.inject(showOpenDialogInjectable);
return async (dialogOptions) => { return async (dialogOptions) => {
await showApplicationWindow(); await showApplicationWindow();
const { canceled, filePaths } = await dialog.showOpenDialog( const { canceled, filePaths } = await showOpenDialog(
dialogOptions, dialogOptions,
); );
return { canceled, filePaths }; if (canceled) {
return {
canceled,
};
}
return {
canceled: false,
paths: filePaths,
};
}; };
}, },
causesSideEffects: true,
}); });
export default askUserForFilePathsInjectable; export default askUserForFilePathsInjectable;

View File

@ -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<Runnable>({
id: "before-frame-starts",
});

View File

@ -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;

View File

@ -28,16 +28,11 @@ import commandOverlayInjectable from "./components/command-palette/command-overl
import { Router } from "react-router"; import { Router } from "react-router";
import historyInjectable from "./navigation/history.injectable"; import historyInjectable from "./navigation/history.injectable";
import themeStoreInjectable from "./themes/store.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 hotbarStoreInjectable from "../common/hotbars/store.injectable";
import openDeleteClusterDialogInjectable from "./components/delete-cluster-dialog/open.injectable"; import openDeleteClusterDialogInjectable from "./components/delete-cluster-dialog/open.injectable";
import kubernetesClusterCategoryInjectable from "../common/catalog/categories/kubernetes-cluster.injectable";
import assert from "assert"; import assert from "assert";
import startFrameInjectable from "./start-frame/start-frame.injectable"; import startFrameInjectable from "./start-frame/start-frame.injectable";
import loggerInjectable from "../common/logger.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) { export async function bootstrap(di: DiContainer) {
const startFrame = di.inject(startFrameInjectable); const startFrame = di.inject(startFrameInjectable);
@ -50,15 +45,6 @@ export async function bootstrap(di: DiContainer) {
assert(rootElem, "#app MUST exist"); 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`); logger.info(`${logPrefix} initializing Catalog`);
initializers.initCatalog({ initializers.initCatalog({
openCommandDialog: di.inject(commandOverlayInjectable).open, openCommandDialog: di.inject(commandOverlayInjectable).open,

View File

@ -3,12 +3,14 @@
* 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 { withInjectables } from "@ogre-tools/injectable-react";
import type { FileFilter, OpenDialogOptions } from "electron"; import type { FileFilter, OpenDialogOptions } from "electron";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import React from "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 { cssNames } from "../../utils";
import { Button } from "../button"; import { Button } from "../button";
import { requestOpenFilePickingDialog } from "../../ipc";
export interface PathPickOpts { export interface PathPickOpts {
label: string; label: string;
@ -26,40 +28,36 @@ export interface PathPickerProps extends PathPickOpts {
disabled?: boolean; disabled?: boolean;
} }
@observer interface Dependencies {
export class PathPicker extends React.Component<PathPickerProps> { openPathPickingDialog: OpenPathPickingDialog;
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 (
<Button
primary
label={label}
disabled={disabled}
className={cssNames("PathPicker", className)}
onClick={() => void this.onClick()}
/>
);
}
} }
const NonInjectedPathPicker = observer((props: PathPickerProps & Dependencies) => {
const {
className,
label,
disabled,
openPathPickingDialog,
...pickOpts
} = props;
return (
<Button
primary
label={label}
disabled={disabled}
className={cssNames("PathPicker", className)}
onClick={() => void openPathPickingDialog({
label,
...pickOpts,
})}
/>
);
});
export const PathPicker = withInjectables<Dependencies, PathPickerProps>(NonInjectedPathPicker, {
getProps: (di, props) => ({
...props,
openPathPickingDialog: di.inject(openPathPickingDialogInjectable),
}),
});

View File

@ -1,79 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { KubernetesClusterCategory } from "../../common/catalog-entities";
import { PathPicker } from "../components/path-picker";
interface Dependencies {
navigateToAddCluster: () => void;
addSyncEntries: (filePaths: string[]) => void;
kubernetesClusterCategory: KubernetesClusterCategory;
isWindows: boolean;
isLinux: boolean;
}
export function initCatalogCategoryRegistryEntries({
navigateToAddCluster,
addSyncEntries,
kubernetesClusterCategory,
isWindows,
isLinux,
} : Dependencies) {
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: async () => {
await PathPicker.pick({
label: "Sync folder(s)",
buttonLabel: "Sync",
properties: ["showHiddenFiles", "multiSelections", "openDirectory"],
onPick: addSyncEntries,
});
},
},
{
icon: "note_add",
title: "Sync kubeconfig file(s)",
onClick: async () => {
await PathPicker.pick({
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: async () => {
await PathPicker.pick({
label: "Sync file(s)",
buttonLabel: "Sync",
properties: ["showHiddenFiles", "multiSelections", "openFile", "openDirectory"],
onPick: addSyncEntries,
});
},
},
);
}
});
}

View File

@ -5,4 +5,3 @@
export * from "./catalog"; export * from "./catalog";
export * from "./ipc"; export * from "./ipc";
export * from "./catalog-category-registry";

View File

@ -3,11 +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 type { OpenDialogOptions } from "electron";
import { clusterActivateHandler, clusterDisconnectHandler, clusterSetFrameIdHandler, clusterStates } from "../../common/ipc/cluster"; import { clusterActivateHandler, clusterDisconnectHandler, clusterSetFrameIdHandler, clusterStates } from "../../common/ipc/cluster";
import type { ClusterId, ClusterState } from "../../common/cluster-types"; import type { ClusterId, ClusterState } from "../../common/cluster-types";
import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel, type WindowAction } from "../../common/ipc/window"; import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel, type WindowAction } from "../../common/ipc/window";
import { openFilePickingDialogChannel } from "../../common/ipc/dialog";
import { extensionDiscoveryStateChannel, extensionLoaderFromMainChannel } from "../../common/ipc/extension-handling"; import { extensionDiscoveryStateChannel, extensionLoaderFromMainChannel } from "../../common/ipc/extension-handling";
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";
@ -44,10 +42,6 @@ export function requestWindowAction(type: WindowAction): Promise<void> {
return requestMain(windowActionHandleChannel, type); return requestMain(windowActionHandleChannel, type);
} }
export function requestOpenFilePickingDialog(opts: OpenDialogOptions): Promise<{ canceled: boolean; filePaths: string[] }> {
return requestMain(openFilePickingDialogChannel, opts);
}
export function requestSetClusterFrameId(clusterId: ClusterId): Promise<void> { export function requestSetClusterFrameId(clusterId: ClusterId): Promise<void> {
return requestMain(clusterSetFrameIdHandler, clusterId); return requestMain(clusterSetFrameIdHandler, clusterId);
} }