diff --git a/src/common/utils/index.ts b/src/common/utils/index.ts index aa135273aa..6035b9f408 100644 --- a/src/common/utils/index.ts +++ b/src/common/utils/index.ts @@ -26,11 +26,6 @@ export function noop(...args: T): void { return void args; } -export enum ControlFlow { - Stop, - Continue -} - export * from "./app-version"; export * from "./autobind"; export * from "./base64"; diff --git a/src/renderer/components/app.scss b/src/renderer/components/app.scss index 8062e38f0b..e1d002c8f7 100755 --- a/src/renderer/components/app.scss +++ b/src/renderer/components/app.scss @@ -172,6 +172,7 @@ code { padding: 0.2em; vertical-align: middle; border-radius: $radius; + font-family: $font-monospace; font-size: calc(var(--font-size) * .9); color: #b4b5b4; diff --git a/src/renderer/components/confirm-dialog/confirm-dialog.tsx b/src/renderer/components/confirm-dialog/confirm-dialog.tsx index 875096db7a..a4a8bdcddf 100644 --- a/src/renderer/components/confirm-dialog/confirm-dialog.tsx +++ b/src/renderer/components/confirm-dialog/confirm-dialog.tsx @@ -22,20 +22,17 @@ import "./confirm-dialog.scss"; import React, { ReactNode } from "react"; -import { action, observable } from "mobx"; +import { observable, makeObservable } from "mobx"; import { observer } from "mobx-react"; import { cssNames, noop, prevDefault } from "../../utils"; import { Button, ButtonProps } from "../button"; import { Dialog, DialogProps } from "../dialog"; import { Icon } from "../icon"; +import { Notifications } from "../notifications"; export interface ConfirmDialogProps extends Partial { } -export interface ConfirmDialogRootProps { - className?: string; -} - export interface ConfirmDialogParams extends ConfirmDialogBooleanParams { ok?: () => any | Promise; cancel?: () => any | Promise; @@ -50,24 +47,23 @@ export interface ConfirmDialogBooleanParams { cancelButtonProps?: Partial; } -const dialogState = observable.set([], { deep: false }); +const dialogState = observable.object({ + isOpen: false, + params: null as ConfirmDialogParams, +}); @observer -export class ConfirmDialog extends React.Component { - static defaultParams: Partial = { - ok: noop, - cancel: noop, - labelOk: "Ok", - labelCancel: "Cancel", - icon: , - }; +export class ConfirmDialog extends React.Component { + @observable isSaving = false; + + constructor(props: ConfirmDialogProps) { + super(props); + makeObservable(this); + } - @action static open(params: ConfirmDialogParams) { - dialogState.add({ - ...ConfirmDialog.defaultParams, - ...params, - }); + dialogState.isOpen = true; + dialogState.params = params; } static confirm(params: ConfirmDialogBooleanParams): Promise { @@ -80,62 +76,92 @@ export class ConfirmDialog extends React.Component { }); } - ok = async (params: ConfirmDialogParams) => { + static defaultParams: Partial = { + ok: noop, + cancel: noop, + labelOk: "Ok", + labelCancel: "Cancel", + icon: , + }; + + get params(): ConfirmDialogParams { + return Object.assign({}, ConfirmDialog.defaultParams, dialogState.params); + } + + ok = async () => { try { - await params.ok(); - } catch {} finally { - dialogState.delete(params); + this.isSaving = true; + await (async () => this.params.ok())(); + } catch (error) { + Notifications.error( + <> +

Confirmation action failed:

+

{error?.message ?? error?.toString?.() ?? "Unknown error"}

+ + ); + } finally { + this.isSaving = false; + dialogState.isOpen = false; } }; - close = async (params: ConfirmDialogParams) => { + onClose = () => { + this.isSaving = false; + }; + + close = async () => { try { - await params.cancel(); - } catch { } finally { - dialogState.delete(params); + await Promise.resolve(this.params.cancel()); + } catch (error) { + Notifications.error( + <> +

Cancelling action failed:

+

{error?.message ?? error?.toString?.() ?? "Unknown error"}

+ + ); + } finally { + this.isSaving = false; + dialogState.isOpen = false; } }; render() { - const { className } = this.props; + const { className, ...dialogProps } = this.props; + const { + icon, labelOk, labelCancel, message, + okButtonProps = {}, + cancelButtonProps = {}, + } = this.params; - return [...dialogState].reduce((prev, params) => { - const { - icon, labelOk, labelCancel, message, - okButtonProps = {}, - cancelButtonProps = {}, - } = params; - - return ( - <> - {prev} - this.close(params)} - > -
- {icon} {message} -
-
-
-
- - ); - }, null); + return ( + +
+ {icon} {message} +
+
+
+
+ ); } } diff --git a/src/renderer/components/delete-cluster-dialog/delete-cluster-dialog.tsx b/src/renderer/components/delete-cluster-dialog/delete-cluster-dialog.tsx index c052f0274c..e9d4bff397 100644 --- a/src/renderer/components/delete-cluster-dialog/delete-cluster-dialog.tsx +++ b/src/renderer/components/delete-cluster-dialog/delete-cluster-dialog.tsx @@ -134,7 +134,7 @@ export class DeleteClusterDialog extends React.Component { className="ml-[1px] mr-[1px]" /> - ) + ); } render() { diff --git a/src/renderer/components/dialog/delete-cluster-dialog.tsx b/src/renderer/components/dialog/delete-cluster-dialog.tsx deleted file mode 100644 index 810ec6606b..0000000000 --- a/src/renderer/components/dialog/delete-cluster-dialog.tsx +++ /dev/null @@ -1,77 +0,0 @@ -/** - * 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 type { KubeConfig } from "@kubernetes/client-node"; -import React from "react"; -import type { Cluster } from "../../../main/cluster"; -import { ControlFlow, iter } from "../../utils"; -import { ConfirmDialog } from "../confirm-dialog"; -import { Select } from "../select"; - -export interface DeleteClusterDialogArgs { - config: KubeConfig; - cluster: Cluster; -} - -export async function deleteClusterConfirmDialog({ config, cluster }: DeleteClusterDialogArgs): Promise<[ControlFlow.Stop] | [ControlFlow.Continue, string | false]> { - const contextNames = new Set(config.getContexts().map(({ name }) => name)); - - contextNames.delete(cluster.contextName); - - if (config.currentContext !== cluster.contextName || contextNames.size === 0) { - return [ControlFlow.Continue, false]; - } - - const options = [ - { - label: "--unset current-context--", - value: false, - }, - ...iter.map(contextNames, name => ({ - label: name, - value: name, - })), - ]; - let selectedOption: string | false = false; - const didConfirm = await ConfirmDialog.confirm({ - labelOk: "Select context", - message: ( - <> -

- The context you are deleting is the current-context in the {cluster.kubeConfigPath} file. - Please select one of the other contexts to replace it with. -

-
-