From 30f59655f07883c798ca2875417df8b2d204a15f Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Thu, 10 Dec 2020 15:59:37 -0500 Subject: [PATCH] add confirm to internal install extension route Signed-off-by: Sebastian Malton --- .../protocol-endpoints/install-extension.ts | 2 +- .../components/+extensions/extensions.tsx | 29 +++++++++++++++---- .../confirm-dialog/confirm-dialog.tsx | 21 ++++++++++---- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/renderer/api/protocol-endpoints/install-extension.ts b/src/renderer/api/protocol-endpoints/install-extension.ts index cd925b03e9..1072e5f1a2 100644 --- a/src/renderer/api/protocol-endpoints/install-extension.ts +++ b/src/renderer/api/protocol-endpoints/install-extension.ts @@ -13,7 +13,7 @@ export async function installExtension(params: RouteParams): Promise { try { navigate(extensionsURL()); - await installFromNpm(name); + await installFromNpm(name, true); } catch (error) { logger.error("[PH - Install Extension]: failed to install from NPM", error); } diff --git a/src/renderer/components/+extensions/extensions.tsx b/src/renderer/components/+extensions/extensions.tsx index 54b510d626..e9936379a4 100644 --- a/src/renderer/components/+extensions/extensions.tsx +++ b/src/renderer/components/+extensions/extensions.tsx @@ -30,6 +30,7 @@ interface InstallRequest { fileName: string; filePath?: string; data?: Buffer; + confirmInstall?: boolean; } interface InstallRequestPreloaded extends InstallRequest { @@ -164,6 +165,10 @@ async function requestInstall(init: InstallRequest | InstallRequest[]) { const folderExists = await fse.pathExists(extensionFolder); if (!folderExists) { + if (install.confirmInstall && !(await confirmInstallExtension(install.manifest))) { + continue; + } + // auto-install extension if not yet exists return unpackExtension(install); } else { @@ -309,6 +314,20 @@ async function uninstallExtension(extension: InstalledExtension) { } } +function confirmInstallExtension({name, version}: LensExtensionManifest): Promise { + const displayName = extensionDisplayName(name, version); + + return new Promise(resolve => { + ConfirmDialog.open({ + message:

Are you sure you want to install extension {displayName}?

, + labelOk: Yes, + labelCancel: No, + ok: () => resolve(true), + cancel: () => resolve(false), + }); + }); +} + function confirmUninstallExtension(extension: InstalledExtension) { const displayName = extensionDisplayName(extension.manifest.name, extension.manifest.version); @@ -357,15 +376,15 @@ async function installFromSelectFileDialog() { * Start extension install using a package name, which is resolved to a tarball url using the npm registry. * @param packageName e.g. "@publisher/extension-name" */ -export async function installFromNpm(packageName: string) { +export async function installFromNpm(packageName: string, confirm = false) { const tarballUrl = await extensionLoader.getNpmPackageTarballUrl(packageName, "@hackweek"); Notifications.info(`Installing ${packageName}`); - return installFromUrlOrPath(tarballUrl); + return installFromUrlOrPath(tarballUrl, confirm); } -async function installFromUrlOrPath(installPath: string) { +async function installFromUrlOrPath(installPath: string, confirmInstall = false) { ExtensionStateStore.getInstance().startingInstall = true; const fileName = path.basename(installPath); @@ -376,11 +395,11 @@ async function installFromUrlOrPath(installPath: string) { const { promise: filePromise } = downloadFile({ url: installPath, timeout: 60000 /*1m*/ }); const data = await filePromise; - await requestInstall({ fileName, data }); + await requestInstall({ fileName, data, confirmInstall }); } // otherwise installing from system path else if (InputValidators.isPath.validate(installPath)) { - await requestInstall({ fileName, filePath: installPath }); + await requestInstall({ fileName, filePath: installPath, confirmInstall }); } } catch (error) { ExtensionStateStore.getInstance().startingInstall = false; diff --git a/src/renderer/components/confirm-dialog/confirm-dialog.tsx b/src/renderer/components/confirm-dialog/confirm-dialog.tsx index f5cfbd42c3..a43918310b 100644 --- a/src/renderer/components/confirm-dialog/confirm-dialog.tsx +++ b/src/renderer/components/confirm-dialog/confirm-dialog.tsx @@ -4,7 +4,7 @@ import React, { ReactNode } from "react"; import { observable } from "mobx"; import { observer } from "mobx-react"; import { Trans } from "@lingui/macro"; -import { cssNames, noop, prevDefault } from "../../utils"; +import { cssNames, prevDefault } from "../../utils"; import { Button, ButtonProps } from "../button"; import { Dialog, DialogProps } from "../dialog"; import { Icon } from "../icon"; @@ -13,7 +13,8 @@ export interface ConfirmDialogProps extends Partial { } export interface ConfirmDialogParams { - ok?: () => void; + ok?: () => void | Promise | Promise; + cancel?: () => void | Promise | Promise; labelOk?: ReactNode; labelCancel?: ReactNode; message?: ReactNode; @@ -39,7 +40,6 @@ export class ConfirmDialog extends React.Component { } public defaultParams: ConfirmDialogParams = { - ok: noop, labelOk: Ok, labelCancel: Cancel, icon: , @@ -52,18 +52,27 @@ export class ConfirmDialog extends React.Component { ok = async () => { try { this.isSaving = true; - await Promise.resolve(this.params.ok()).catch(noop); + await this.params.ok?.(); + } catch { + // ignore } finally { this.isSaving = false; } - this.close(); + ConfirmDialog.close(); }; onClose = () => { this.isSaving = false; }; - close = () => { + close = async () => { + try { + await this.params.cancel?.(); + } catch { + // ignore + } finally { + this.isSaving = false; + } ConfirmDialog.close(); };