mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Open file picker dialog first before switching to preferences page
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
207f0dfd62
commit
3d06f8a765
@ -20,12 +20,11 @@
|
||||
*/
|
||||
|
||||
import { catalogCategoryRegistry } from "../catalog/catalog-category-registry";
|
||||
import { CatalogEntity, CatalogEntityActionContext, CatalogEntityAddMenuContext, CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog";
|
||||
import { CatalogEntity, CatalogEntityActionContext, CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog";
|
||||
import { clusterActivateHandler, clusterDeleteHandler, clusterDisconnectHandler } from "../cluster-ipc";
|
||||
import { ClusterStore } from "../cluster-store";
|
||||
import { requestMain } from "../ipc";
|
||||
import { CatalogCategory, CatalogCategorySpec } from "../catalog";
|
||||
import { addClusterURL, preferencesURL } from "../routes";
|
||||
import { app } from "electron";
|
||||
import type { CatalogEntitySpec } from "../catalog/catalog-entity";
|
||||
import { HotbarStore } from "../hotbar-store";
|
||||
@ -148,7 +147,7 @@ export class KubernetesCluster extends CatalogEntity<KubernetesClusterMetadata,
|
||||
}
|
||||
}
|
||||
|
||||
export class KubernetesClusterCategory extends CatalogCategory {
|
||||
class KubernetesClusterCategory extends CatalogCategory {
|
||||
public readonly apiVersion = "catalog.k8slens.dev/v1alpha1";
|
||||
public readonly kind = "CatalogCategory";
|
||||
public metadata = {
|
||||
@ -167,25 +166,8 @@ export class KubernetesClusterCategory extends CatalogCategory {
|
||||
kind: "KubernetesCluster"
|
||||
}
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.on("catalogAddMenu", (ctx: CatalogEntityAddMenuContext) => {
|
||||
ctx.menuItems.push(
|
||||
{
|
||||
icon: "text_snippet",
|
||||
title: "Add from kubeconfig",
|
||||
onClick: () => ctx.navigate(addClusterURL()),
|
||||
},
|
||||
{
|
||||
icon: "settings",
|
||||
title: "Sync kubeconfig file(s)",
|
||||
onClick: () => ctx.navigate(preferencesURL({ fragment: "kube-sync" })),
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
catalogCategoryRegistry.add(new KubernetesClusterCategory());
|
||||
export const kubernetesClusterCategory = new KubernetesClusterCategory();
|
||||
|
||||
catalogCategoryRegistry.add(kubernetesClusterCategory);
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
import EventEmitter from "events";
|
||||
import type TypedEmitter from "typed-emitter";
|
||||
import { observable, makeObservable } from "mobx";
|
||||
import type React from "react";
|
||||
|
||||
type ExtractEntityMetadataType<Entity> = Entity extends CatalogEntity<infer Metadata> ? Metadata : never;
|
||||
type ExtractEntityStatusType<Entity> = Entity extends CatalogEntity<any, infer Status> ? Status : never;
|
||||
@ -108,7 +109,7 @@ export interface CatalogEntityContextMenu {
|
||||
/**
|
||||
* Menu icon
|
||||
*/
|
||||
icon?: string;
|
||||
icon?: string | React.ComponentType;
|
||||
/**
|
||||
* OnClick handler
|
||||
*/
|
||||
@ -122,7 +123,7 @@ export interface CatalogEntityContextMenu {
|
||||
}
|
||||
|
||||
export interface CatalogEntityAddMenu extends CatalogEntityContextMenu {
|
||||
icon: string;
|
||||
icon: string | React.ComponentType;
|
||||
}
|
||||
|
||||
export interface CatalogEntitySettingsMenu {
|
||||
|
||||
@ -21,6 +21,12 @@
|
||||
|
||||
import { action, ObservableMap } from "mobx";
|
||||
|
||||
export function multiSet<T, V>(map: Map<T, V>, newEntries: [T, V][]): void {
|
||||
for (const [key, val] of newEntries) {
|
||||
map.set(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtendedMap<K, V> extends Map<K, V> {
|
||||
static new<K, V>(entries?: readonly (readonly [K, V])[] | null): ExtendedMap<K, V> {
|
||||
return new ExtendedMap<K, V>(entries);
|
||||
|
||||
@ -74,6 +74,7 @@ export async function bootstrap(App: AppComponent) {
|
||||
rootElem.classList.toggle("is-mac", isMac);
|
||||
|
||||
initializers.initRegistries();
|
||||
initializers.initCatalogCategoryRegistryEntries();
|
||||
initializers.initAppPreferenceKindRegistry();
|
||||
initializers.initAppPreferenceRegistry();
|
||||
initializers.initCommandRegistry();
|
||||
|
||||
@ -97,7 +97,7 @@ export class CatalogAddButton extends React.Component<CatalogAddButtonProps> {
|
||||
{ this.menuItems.map((menuItem, index) => {
|
||||
return <SpeedDialAction
|
||||
key={index}
|
||||
icon={<Icon material={menuItem.icon} />}
|
||||
icon={typeof menuItem.icon === "string" ? <Icon material={menuItem.icon} /> : <menuItem.icon />}
|
||||
tooltipTitle={menuItem.title}
|
||||
onClick={() => menuItem.onClick()}
|
||||
TooltipClasses={{
|
||||
|
||||
@ -31,11 +31,24 @@ import { ConfirmDialog } from "../confirm-dialog";
|
||||
import { HotbarStore } from "../../../common/hotbar-store";
|
||||
import { Icon } from "../icon";
|
||||
import type { CatalogEntityItem } from "./catalog-entity.store";
|
||||
import { Tooltip } from "@material-ui/core";
|
||||
|
||||
export interface CatalogEntityDrawerMenuProps<T extends CatalogEntity> extends MenuActionsProps {
|
||||
item: CatalogEntityItem<T> | null | undefined;
|
||||
}
|
||||
|
||||
function resolveIcon(item: CatalogEntityContextMenu) {
|
||||
if (typeof item.icon === "string") {
|
||||
if (item.icon.includes("<svg")) {
|
||||
return <Icon title={item.title} svg={item.icon} />;
|
||||
}
|
||||
|
||||
return <Icon title={item.title} material={item.icon} />;
|
||||
}
|
||||
|
||||
return <Tooltip title={item.title}><item.icon /></Tooltip>;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class CatalogEntityDrawerMenu<T extends CatalogEntity> extends React.Component<CatalogEntityDrawerMenuProps<T>> {
|
||||
@observable private contextMenu: CatalogEntityContextMenuContext;
|
||||
@ -86,14 +99,9 @@ export class CatalogEntityDrawerMenu<T extends CatalogEntity> extends React.Comp
|
||||
continue;
|
||||
}
|
||||
|
||||
const key = menuItem.icon.includes("<svg") ? "svg" : "material";
|
||||
|
||||
items.push(
|
||||
<MenuItem key={menuItem.title} onClick={() => this.onMenuItemClick(menuItem)}>
|
||||
<Icon
|
||||
tooltip={menuItem.title}
|
||||
{...{ [key]: menuItem.icon }}
|
||||
/>
|
||||
{resolveIcon(menuItem)}
|
||||
</MenuItem>
|
||||
);
|
||||
}
|
||||
@ -109,7 +117,7 @@ export class CatalogEntityDrawerMenu<T extends CatalogEntity> extends React.Comp
|
||||
|
||||
render() {
|
||||
const { className, item: entity, ...menuProps } = this.props;
|
||||
|
||||
|
||||
if (!this.contextMenu || !entity.enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ import fse from "fs-extra";
|
||||
import { KubeconfigSyncEntry, KubeconfigSyncValue, UserStore } from "../../../common/user-store";
|
||||
import { Spinner } from "../spinner";
|
||||
import logger from "../../../main/logger";
|
||||
import { iter } from "../../utils";
|
||||
import { iter, multiSet } from "../../utils";
|
||||
import { isWindows } from "../../../common/vars";
|
||||
import { PathPicker } from "../path-picker";
|
||||
|
||||
@ -68,6 +68,10 @@ async function getMapEntry({ filePath, ...data}: KubeconfigSyncEntry): Promise<[
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAllEntries(filePaths: string[]): Promise<[string, Value][]> {
|
||||
return Promise.all(filePaths.map(filePath => getMapEntry({ filePath })));
|
||||
}
|
||||
|
||||
@observer
|
||||
export class KubeconfigSyncs extends React.Component {
|
||||
syncs = observable.map<string, Value>();
|
||||
@ -105,13 +109,7 @@ export class KubeconfigSyncs extends React.Component {
|
||||
}
|
||||
|
||||
@action
|
||||
onPick = async (filePaths: string[]) => {
|
||||
const newEntries = await Promise.all(filePaths.map(filePath => getMapEntry({ filePath })));
|
||||
|
||||
for (const [filePath, info] of newEntries) {
|
||||
this.syncs.set(filePath, info);
|
||||
}
|
||||
};
|
||||
onPick = async (filePaths: string[]) => multiSet(this.syncs, await getAllEntries(filePaths));
|
||||
|
||||
renderEntryIcon(entry: Entry) {
|
||||
switch (entry.info.type) {
|
||||
|
||||
@ -25,12 +25,10 @@ import React from "react";
|
||||
import { cssNames } from "../../utils";
|
||||
import { Button } from "../button";
|
||||
|
||||
export interface PathPickerProps {
|
||||
className?: string;
|
||||
export interface PathPickOpts {
|
||||
label: string;
|
||||
disabled?: boolean;
|
||||
onPick?: (paths: string[]) => void;
|
||||
onCancel?: () => void;
|
||||
onPick?: (paths: string[]) => any;
|
||||
onCancel?: () => any;
|
||||
defaultPath?: string;
|
||||
buttonLabel?: string;
|
||||
filters?: FileFilter[];
|
||||
@ -38,10 +36,15 @@ export interface PathPickerProps {
|
||||
securityScopedBookmarks?: boolean;
|
||||
}
|
||||
|
||||
export interface PathPickerProps extends PathPickOpts {
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class PathPicker extends React.Component<PathPickerProps> {
|
||||
async onClick() {
|
||||
const { onPick, onCancel, label, className, disabled, ...dialogOptions } = this.props;
|
||||
static async pick(opts: PathPickOpts) {
|
||||
const { onPick, onCancel, label, ...dialogOptions } = opts;
|
||||
const { dialog, BrowserWindow } = remote;
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog(BrowserWindow.getFocusedWindow(), {
|
||||
message: label,
|
||||
@ -49,12 +52,18 @@ export class PathPicker extends React.Component<PathPickerProps> {
|
||||
});
|
||||
|
||||
if (canceled) {
|
||||
onCancel?.();
|
||||
await onCancel?.();
|
||||
} else {
|
||||
onPick?.(filePaths);
|
||||
await onPick?.(filePaths);
|
||||
}
|
||||
}
|
||||
|
||||
async onClick() {
|
||||
const { className, disabled, ...pickOpts } = this.props;
|
||||
|
||||
return PathPicker.pick(pickOpts);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, label, disabled } = this.props;
|
||||
|
||||
|
||||
98
src/renderer/initializers/catalog-category-registry.tsx
Normal file
98
src/renderer/initializers/catalog-category-registry.tsx
Normal file
@ -0,0 +1,98 @@
|
||||
/**
|
||||
* Copyright (c) 2021 OpenLens Authors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { CreateNewFolderOutlined, NoteAdd } from "@material-ui/icons";
|
||||
import { kubernetesClusterCategory } from "../../common/catalog-entities";
|
||||
import { addClusterURL, preferencesURL } from "../../common/routes";
|
||||
import { PathPicker } from "../components/path-picker";
|
||||
import { multiSet } from "../utils";
|
||||
import { UserStore } from "../../common/user-store";
|
||||
import { getAllEntries } from "../components/+preferences/kubeconfig-syncs";
|
||||
import { runInAction } from "mobx";
|
||||
import { isWindows } from "../../common/vars";
|
||||
|
||||
async function addSyncEntries(filePaths: string[]) {
|
||||
const entries = await getAllEntries(filePaths);
|
||||
|
||||
runInAction(() => {
|
||||
multiSet(UserStore.getInstance().syncKubeconfigEntries, entries);
|
||||
});
|
||||
}
|
||||
|
||||
export function initCatalogCategoryRegistryEntries() {
|
||||
kubernetesClusterCategory.on("catalogAddMenu", ctx => {
|
||||
ctx.menuItems.push(
|
||||
{
|
||||
icon: "text_snippet",
|
||||
title: "Add from kubeconfig",
|
||||
onClick: () => ctx.navigate(addClusterURL()),
|
||||
},
|
||||
);
|
||||
|
||||
if (isWindows) {
|
||||
ctx.menuItems.push(
|
||||
{
|
||||
icon: () => <CreateNewFolderOutlined fontSize="large" />,
|
||||
title: "Sync kubeconfig folders(s)",
|
||||
onClick: async () => {
|
||||
await PathPicker.pick({
|
||||
label: "Sync folders(s)",
|
||||
buttonLabel: "Sync",
|
||||
properties: ["showHiddenFiles", "multiSelections", "openDirectory"],
|
||||
onPick: addSyncEntries,
|
||||
});
|
||||
ctx.navigate(preferencesURL({ fragment: "kube-sync" }));
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: () => <NoteAdd fontSize="large" />,
|
||||
title: "Sync kubeconfig file(s)",
|
||||
onClick: async () => {
|
||||
await PathPicker.pick({
|
||||
label: "Sync file(s)",
|
||||
buttonLabel: "Sync",
|
||||
properties: ["showHiddenFiles", "multiSelections", "openFile"],
|
||||
onPick: addSyncEntries,
|
||||
});
|
||||
ctx.navigate(preferencesURL({ fragment: "kube-sync" }));
|
||||
},
|
||||
},
|
||||
);
|
||||
} else {
|
||||
ctx.menuItems.push(
|
||||
{
|
||||
icon: "settings",
|
||||
title: "Sync kubeconfig(s)",
|
||||
onClick: async () => {
|
||||
await PathPicker.pick({
|
||||
label: "Sync file(s)",
|
||||
buttonLabel: "Sync",
|
||||
properties: ["showHiddenFiles", "multiSelections", "openFile"],
|
||||
onPick: addSyncEntries,
|
||||
});
|
||||
ctx.navigate(preferencesURL({ fragment: "kube-sync" }));
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -21,6 +21,7 @@
|
||||
|
||||
export * from "./app-preferences-kind-registry";
|
||||
export * from "./app-preferences-registry";
|
||||
export * from "./catalog-category-registry";
|
||||
export * from "./catalog-entity-detail-registry";
|
||||
export * from "./catalog";
|
||||
export * from "./command-registry";
|
||||
|
||||
Loading…
Reference in New Issue
Block a user