UNPKG

teamsfx-extension

Version:

Create, debug, and deploy Teams apps with Teams Toolkit

275 lines 13.1 kB
"use strict"; // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.WebviewPanel = void 0; const path = require("path"); const vscode = require("vscode"); const extensionVariables_1 = require("../extensionVariables"); const Commands_1 = require("./Commands"); const axios_1 = require("axios"); const AdmZip = require("adm-zip"); const fs = require("fs-extra"); const azureLogin_1 = require("../commonlib/azureLogin"); const appStudioLogin_1 = require("../commonlib/appStudioLogin"); const handlers_1 = require("../handlers"); const teamsfx_api_1 = require("@microsoft/teamsfx-api"); const PanelType_1 = require("./PanelType"); const child_process_1 = require("child_process"); const commonUtils_1 = require("../utils/commonUtils"); const userInterface_1 = require("../userInterface"); class WebviewPanel { constructor(extensionPath, panelType, column) { this.panelType = PanelType_1.PanelType.QuickStart; this.disposables = []; this.isValidNode = () => { var _a; try { const supportedVersions = ["10", "12", "14"]; const output = child_process_1.execSync("node --version"); const regex = /v(?<major_version>\d+)\.(?<minor_version>\d+)\.(?<patch_version>\d+)/gm; const match = regex.exec(output.toString()); if (!match) { return false; } const majorVersion = (_a = match.groups) === null || _a === void 0 ? void 0 : _a.major_version; if (!majorVersion) { return false; } return supportedVersions.includes(majorVersion); } catch (e) { } return false; }; this.extensionPath = extensionPath; this.panelType = panelType; // Create and show a new webview panel this.panel = vscode.window.createWebviewPanel(WebviewPanel.viewType, this.getWebpageTitle(panelType), column, { // Enable javascript in the webview enableScripts: true, retainContextWhenHidden: true, localResourceRoots: [vscode.Uri.file(path.join(this.extensionPath, "out"))], }); // Listen for when the panel is disposed // This happens when the user closes the panel or when the panel is closed programatically this.panel.onDidDispose(() => this.dispose(), null, this.disposables); // Handle messages from the webview this.panel.webview.onDidReceiveMessage((msg) => __awaiter(this, void 0, void 0, function* () { switch (msg.command) { case Commands_1.Commands.OpenExternalLink: vscode.env.openExternal(vscode.Uri.parse(msg.data)); break; case Commands_1.Commands.CloneSampleApp: const selection = yield vscode.window.showInformationMessage(`Download '${msg.data.appName}' from Github. This will download '${msg.data.appName}' repository and open to your local machine`, { modal: false }, "Download", "Cancel"); if (selection === "Download") { const folder = yield vscode.window.showOpenDialog({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, title: "Select folder to download the sample app", }); if (folder !== undefined) { const sampleAppPath = path.join(folder[0].fsPath, msg.data.appFolder); if ((yield fs.pathExists(sampleAppPath)) && (yield fs.readdir(sampleAppPath)).length > 0) { vscode.window.showErrorMessage(`Path ${sampleAppPath} alreay exists. Select a different folder.`); return; } const dialogManager = userInterface_1.DialogManager.getInstance(); const progress = dialogManager.createProgressBar("Fetch sample app", 2); progress.start(); try { progress.next(`Downloading from '${msg.data.appUrl}'`); const result = yield this.fetchCodeZip(msg.data.appUrl); progress.next("Unzipping the sample package"); if (result !== undefined) { yield this.saveFilesRecursively(new AdmZip(result.data), msg.data.appFolder, folder[0].fsPath); vscode.commands.executeCommand("vscode.openFolder", vscode.Uri.file(sampleAppPath)); } else { vscode.window.showErrorMessage("Failed to download sample app"); } } finally { progress.end(); } } } break; case Commands_1.Commands.DisplayCommandPalette: break; case Commands_1.Commands.DisplayCommands: vscode.commands.executeCommand("workbench.action.quickOpen", `>${msg.data}`); break; case Commands_1.Commands.SigninM365: yield appStudioLogin_1.default.getJsonObject(false); break; case Commands_1.Commands.SigninAzure: vscode.commands.executeCommand("fx-extension.signinAzure", ["webview", false]); break; case Commands_1.Commands.CreateNewProject: yield handlers_1.runCommand(teamsfx_api_1.Stage.create); break; case Commands_1.Commands.SwitchPanel: WebviewPanel.createOrShow(this.extensionPath, msg.data); break; case Commands_1.Commands.InitAccountInfo: this.setStatusChangeMap(); break; default: break; } }), undefined, extensionVariables_1.ext.context.subscriptions); // Set the webview's initial html content this.panel.webview.html = this.getHtmlForWebview(panelType); } static createOrShow(extensionPath, panelType) { const column = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.viewColumn : undefined; if (WebviewPanel.currentPanels && WebviewPanel.currentPanels.findIndex((panel) => panel.panelType === panelType) > -1) { WebviewPanel.currentPanels .find((panel) => panel.panelType === panelType) .panel.reveal(column); } else { WebviewPanel.currentPanels.push(new WebviewPanel(extensionPath, panelType, column || vscode.ViewColumn.One)); } } getWebpageTitle(panelType) { switch (panelType) { case PanelType_1.PanelType.QuickStart: return "Quick Start"; case PanelType_1.PanelType.SampleGallery: return "Samples"; } } setStatusChangeMap() { appStudioLogin_1.default.setStatusChangeMap("quick-start-webview", (status, token, accountInfo) => { let email = undefined; if (status === "SignedIn") { email = accountInfo.upn ? accountInfo.upn : undefined; } if (this.panel && this.panel.webview) { this.panel.webview.postMessage({ message: "m365AccountChange", data: email, }); } return Promise.resolve(); }); azureLogin_1.default.setStatusChangeMap("quick-start-webview", (status, token, accountInfo) => { let email = undefined; if (status === "SignedIn") { const token = azureLogin_1.default.getAccountCredential(); if (token !== undefined) { email = token.username ? token.username : undefined; } } if (this.panel && this.panel.webview) { this.panel.webview.postMessage({ message: "azureAccountChange", data: email, }); } return Promise.resolve(); }); } fetchCodeZip(url) { return __awaiter(this, void 0, void 0, function* () { let retries = 3; let result = undefined; while (retries > 0) { retries--; try { result = yield axios_1.default.get(url, { responseType: "arraybuffer", }); if (result.status === 200 || result.status === 201) { return result; } } catch (e) { yield new Promise((resolve) => setTimeout(resolve, 10000)); } } return result; }); } saveFilesRecursively(zip, appFolder, dstPath) { return __awaiter(this, void 0, void 0, function* () { yield Promise.all(zip .getEntries() .filter((entry) => !entry.isDirectory && entry.entryName.includes(appFolder)) .map((entry) => __awaiter(this, void 0, void 0, function* () { const data = entry.getData().toString(); const entryPath = entry.entryName.substring(entry.entryName.indexOf("/") + 1); const filePath = path.join(dstPath, entryPath); yield fs.ensureDir(path.dirname(filePath)); yield fs.writeFile(filePath, data); }))); }); } getHtmlForWebview(panelType) { const scriptBasePathOnDisk = vscode.Uri.file(path.join(this.extensionPath, "out/")); const scriptBaseUri = scriptBasePathOnDisk.with({ scheme: "vscode-resource" }); const scriptPathOnDisk = vscode.Uri.file(path.join(this.extensionPath, "out/src", "client.js")); const scriptUri = scriptPathOnDisk.with({ scheme: "vscode-resource" }); // Use a nonce to to only allow specific scripts to be run const nonce = this.getNonce(); return `<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>ms-teams</title> <base href='${scriptBaseUri}' /> </head> <body> <div id="root"></div> <script> const vscode = acquireVsCodeApi(); const panelType = '${panelType}'; const isSupportedNode = ${this.isValidNode()}; const isMacPlatform = ${commonUtils_1.isMacOS()}; </script> <script nonce="${nonce}" type="module" src="${scriptUri}"></script> </body> </html>`; } getNonce() { let text = ""; const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; for (let i = 0; i < 32; i++) { text += possible.charAt(Math.floor(Math.random() * possible.length)); } return text; } dispose() { WebviewPanel.currentPanels.splice(WebviewPanel.currentPanels.indexOf(this), 1); appStudioLogin_1.default.removeStatusChangeMap("quick-start-webview"); azureLogin_1.default.removeStatusChangeMap("quick-start-webview"); // Clean up our resources this.panel.dispose(); while (this.disposables.length) { const x = this.disposables.pop(); if (x) { x.dispose(); } } } } exports.WebviewPanel = WebviewPanel; WebviewPanel.viewType = "react"; WebviewPanel.currentPanels = []; //# sourceMappingURL=webviewPanel.js.map