diff --git a/images/vscode.svg b/images/vscode.svg new file mode 100644 index 000000000..137be57f0 --- /dev/null +++ b/images/vscode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Explorer/Explorer.tsx b/src/Explorer/Explorer.tsx index a07a7dad3..5c619663f 100644 --- a/src/Explorer/Explorer.tsx +++ b/src/Explorer/Explorer.tsx @@ -282,6 +282,69 @@ export default class Explorer { } } + public openInVsCode(): void { + TelemetryProcessor.traceStart(Action.OpenVSCode); + this.openVsCodeButtonClick(); + } + + private openVsCodeButtonClick(): void { + const activeTab = useTabs.getState().activeTab; + const resourceId = encodeURIComponent(userContext.databaseAccount.id); + const database = encodeURIComponent(activeTab?.collection?.databaseId); + const container = encodeURIComponent(activeTab?.collection?.id()); + const baseUrl = `vscod://ms-azuretools.vscode-cosmosdb?resourceId=${resourceId}`; + const vscodeUrl = activeTab ? `${baseUrl}&database=${database}&container=${container}` : baseUrl; + const startTime = Date.now(); + let vsCodeNotOpened = false; + + setTimeout(() => { + const timeOutTime = Date.now() - startTime; + if (!vsCodeNotOpened && timeOutTime < 1050) { + vsCodeNotOpened = true; + useDialog.getState().openDialog(openVSCodeDialogProps); + } + }, 1000); + + const link = document.createElement("a"); + link.href = vscodeUrl; + link.rel = "noopener noreferrer"; + document.body.appendChild(link); + + try { + link.click(); + document.body.removeChild(link); + TelemetryProcessor.traceStart(Action.OpenVSCode); + } catch (error) { + if (!vsCodeNotOpened) { + vsCodeNotOpened = true; + logConsoleError(`Failed to open VS Code: ${getErrorMessage(error)}`); + } + } + + const openVSCodeDialogProps: DialogProps = { + linkProps: { + linkText: "Download Visual Studio Code", + linkUrl: "https://code.visualstudio.com/download", + }, + isModal: true, + title: `Open your Azure Cosmos DB account in Visual Studio Code`, + subText: `Please ensure Visual Studio Code is installed on your device. + If you don't have it installed, please download it from the link below.`, + primaryButtonText: "Open in VS Code", + secondaryButtonText: "Cancel", + + onPrimaryButtonClick: () => { + vsCodeNotOpened = false; + this.openVsCodeButtonClick(); + useDialog.getState().closeDialog(); + }, + onSecondaryButtonClick: () => { + useDialog.getState().closeDialog(); + TelemetryProcessor.traceCancel(Action.OpenVSCode); + }, + }; + } + public async openCESCVAFeedbackBlade(): Promise { sendMessage({ type: MessageTypes.OpenCESCVAFeedbackBlade }); Logger.logInfo( diff --git a/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx b/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx index f9aa5e54f..fbf1b4434 100644 --- a/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx +++ b/src/Explorer/Menus/CommandBar/CommandBarComponentButtonFactory.tsx @@ -14,6 +14,7 @@ import OpenQueryFromDiskIcon from "../../../../images/OpenQueryFromDisk.svg"; import OpenInTabIcon from "../../../../images/open-in-tab.svg"; import SettingsIcon from "../../../../images/settings_15x15.svg"; import SynapseIcon from "../../../../images/synapse-link.svg"; +import VSCodeIcon from "../../../../images/vscode.svg"; import { AuthType } from "../../../AuthType"; import * as Constants from "../../../Common/Constants"; import { Platform, configContext } from "../../../ConfigContext"; @@ -60,6 +61,10 @@ export function createStaticCommandBarButtons( addDivider(); buttons.push(addSynapseLink); } + if (userContext.apiType !== "Gremlin") { + const addVsCode = createOpenVsCodeDialogButton(container); + buttons.push(addVsCode); + } } if (isDataplaneRbacSupported(userContext.apiType)) { @@ -268,6 +273,18 @@ function createOpenSynapseLinkDialogButton(container: Explorer): CommandButtonCo }; } +function createOpenVsCodeDialogButton(container: Explorer): CommandButtonComponentProps { + const label = "Visual Studio Code"; + return { + iconSrc: VSCodeIcon, + iconAlt: label, + onCommandClick: () => container.openInVsCode(), + commandButtonLabel: label, + hasPopup: false, + ariaLabel: label, + }; +} + function createLoginForEntraIDButton(container: Explorer): CommandButtonComponentProps { if (configContext.platform !== Platform.Portal) { return undefined; @@ -500,6 +517,6 @@ export function createPostgreButtons(container: Explorer): CommandButtonComponen export function createVCoreMongoButtons(container: Explorer): CommandButtonComponentProps[] { const openVCoreMongoTerminalButton = createOpenTerminalButtonByKind(container, ViewModels.TerminalKind.VCoreMongo); - - return [openVCoreMongoTerminalButton]; + const addVsCode = createOpenVsCodeDialogButton(container); + return [openVCoreMongoTerminalButton, addVsCode]; } diff --git a/src/Shared/Telemetry/TelemetryConstants.ts b/src/Shared/Telemetry/TelemetryConstants.ts index c20a49f93..031d061cc 100644 --- a/src/Shared/Telemetry/TelemetryConstants.ts +++ b/src/Shared/Telemetry/TelemetryConstants.ts @@ -149,6 +149,7 @@ export enum Action { UploadDocuments, // Used in Fabric. Please do not rename. CloudShellUserConsent, CloudShellTerminalSession, + OpenVSCode, } export const ActionModifiers = { diff --git a/web.config b/web.config index 8efe7ce6d..f29c8b878 100644 --- a/web.config +++ b/web.config @@ -30,7 +30,7 @@ - +