From e364f92a19da8ea1abf3f3197c0fae87e6a96dc7 Mon Sep 17 00:00:00 2001 From: ramukaritik Date: Fri, 15 Sep 2023 15:20:19 +0530 Subject: [PATCH 1/4] Added co-presence worker script --- src/web/client/services/copresenceWorker.js | 165 ++++++++++++++++++++ src/web/client/telemetry/constants.ts | 3 +- 2 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 src/web/client/services/copresenceWorker.js diff --git a/src/web/client/services/copresenceWorker.js b/src/web/client/services/copresenceWorker.js new file mode 100644 index 00000000..139f075d --- /dev/null +++ b/src/web/client/services/copresenceWorker.js @@ -0,0 +1,165 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import WebExtensionContext from "../WebExtensionContext"; +import { telemetryEventNames } from "../telemetry/constants"; + +// eslint-disable-next-line no-undef +self.window = self; +// eslint-disable-next-line @typescript-eslint/no-var-requires +const fluid = require("fluid-framework"); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const { AzureClient } = require("@fluidframework/azure-client"); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const DataverseTokenProvider = require("../common/DataverseTokenProvider"); + +const { SharedMap, ConnectionState } = fluid; + +const objectTypes = [SharedMap]; + +const containerSchema = { + dynamicObjectTypes: objectTypes, + initialObjects: { + sharedState: SharedMap, + }, +}; + +let initial = false; + +class AzureFluidClient { + static _clientInstance; + static _container; + static _audience; + static _userSharedMap; + + static getInstance(config) { + if (!this._clientInstance) { + const afrClientProps = { + connection: { + type: "remote", + tenantId: config.swptenantId, + tokenProvider: new DataverseTokenProvider( + config.swpAccessToken, + () => this.fetchAccessToken() + ), + endpoint: config.discoveryendpoint, + }, + }; + + AzureFluidClient._clientInstance = new AzureClient(afrClientProps); + } + return this._clientInstance; + } + + static async fetchContainerAndService(config, id) { + if (this._container?.connectionState !== ConnectionState.Connected) { + const azureClient = this.getInstance(config); + const { container, services } = await azureClient.getContainer( + id, + containerSchema + ); + if (container.connectionState !== ConnectionState.Connected) { + await new Promise((resolve) => { + container.once("connected", () => { + resolve(); + }); + }); + } + this._container = container; + this._audience = services.audience; + this._userSharedMap = container.initialObjects.sharedState; + } + return { + container: this._container, + audience: this._audience, + map: this._userSharedMap, + }; + } +} + +async function loadContainer(config, id, swpId, file) { + try { + const { container, audience, map } = + await AzureFluidClient.fetchContainerAndService(config, swpId); + + const existingMembers = audience.getMembers(); + + const myself = audience.getMyself(); + + const currentUser = { + containerId: id, + fileName: file.fileName, + filePath: file.filePath, + userName: myself.userName, + }; + + map.set(myself.userId, currentUser); + + audience.on("memberRemoved", (clientId, member) => { + if (!existingMembers.get(member.userId)) { + self.postMessage({ + type: "member-removed", + userId: member.userId, + }); + } + }); + + if (!initial) { + existingMembers.forEach(async (value, key) => { + const otherUser = value; + + self.postMessage({ + type: "client-data", + userName: otherUser.userName, + userId: key, + containerId: swpId, + fileName: + otherUser.fileName === undefined + ? "On Studio" + : otherUser.fileName, + filePath: otherUser.filePath, + }); + }); + initial = true; + } + + map.on("valueChanged", async (changed, local) => { + if (!local) { + const otherUser = map.get(changed.key); + // eslint-disable-next-line no-undef + await self.postMessage({ + type: "client-data", + userId: changed.key, + userName: otherUser.userName, + containerId: swpId, + fileName: otherUser.fileName, + filePath: otherUser.filePath, + }); + } + }); + } catch (error) { + WebExtensionContext.telemetry.sendErrorTelemetry( + telemetryEventNames.WEB_EXTENSION_AZURE_FLUID_SERVICE_LOAD_ERROR, + error + ); + } +} + +function runFluidApp() { + // Listen for messages from the extension + // eslint-disable-next-line no-undef + self.addEventListener("message", async (event) => { + const message = event.data; + + await loadContainer( + message.afrConfig, + message.containerId, + message.afrConfig.swpId, + message.file + ); + }); +} + +runFluidApp(); diff --git a/src/web/client/telemetry/constants.ts b/src/web/client/telemetry/constants.ts index f93304cf..48e84d0c 100644 --- a/src/web/client/telemetry/constants.ts +++ b/src/web/client/telemetry/constants.ts @@ -91,4 +91,5 @@ export enum telemetryEventNames { WEB_EXTENSION_WEB_COPILOT_REGISTRATION_FAILED = 'webExtensionCopilotRegisterFailed', WEB_EXTENSION_WEB_COPILOT_NOTIFICATION_SHOWN = 'webExtensionCopilotNotificationShown', WEB_EXTENSION_WEB_COPILOT_NOTIFICATION_EVENT_CLICKED = 'webExtensionCopilotNotificationEventClicked', -} \ No newline at end of file + WEB_EXTENSION_AZURE_FLUID_SERVICE_LOAD_ERROR = 'webExtensionAzureFluidServiceLoadError', +} From 255ae67c0264be096a1aad82f9d2c87dd305b787 Mon Sep 17 00:00:00 2001 From: ramukaritik Date: Wed, 20 Sep 2023 03:42:25 +0530 Subject: [PATCH 2/4] TODO add telemetry --- src/web/client/services/copresenceWorker.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/web/client/services/copresenceWorker.js b/src/web/client/services/copresenceWorker.js index 139f075d..346096da 100644 --- a/src/web/client/services/copresenceWorker.js +++ b/src/web/client/services/copresenceWorker.js @@ -140,10 +140,7 @@ async function loadContainer(config, id, swpId, file) { } }); } catch (error) { - WebExtensionContext.telemetry.sendErrorTelemetry( - telemetryEventNames.WEB_EXTENSION_AZURE_FLUID_SERVICE_LOAD_ERROR, - error - ); + // TODO: add telemetry } } From e7e565da95e66d581b05685f790f4eb6cce4cc85 Mon Sep 17 00:00:00 2001 From: ramukaritik Date: Wed, 20 Sep 2023 03:54:24 +0530 Subject: [PATCH 3/4] Treeview logic --- src/web/client/WebExtensionContext.ts | 85 +++++++++++++++++- src/web/client/utilities/urlBuilderUtil.ts | 42 ++++++++- .../client/webViews/TreeWebViewProvider.ts | 88 +++++++++++++++++++ 3 files changed, 212 insertions(+), 3 deletions(-) create mode 100644 src/web/client/webViews/TreeWebViewProvider.ts diff --git a/src/web/client/WebExtensionContext.ts b/src/web/client/WebExtensionContext.ts index 6d975b01..909093ee 100644 --- a/src/web/client/WebExtensionContext.ts +++ b/src/web/client/WebExtensionContext.ts @@ -21,7 +21,7 @@ import { getWebsiteIdToLcidMap, getWebsiteLanguageIdToPortalLanguageIdMap, } from "./utilities/schemaHelperUtil"; -import { getCustomRequestURL } from "./utilities/urlBuilderUtil"; +import { getCustomRequestURL, getOrCreateSharedWorkspace } from "./utilities/urlBuilderUtil"; import { schemaKey } from "./schema/constants"; import { telemetryEventNames } from "./telemetry/constants"; import { EntityDataMap } from "./context/entityDataMap"; @@ -29,6 +29,8 @@ import { FileDataMap } from "./context/fileDataMap"; import { IAttributePath, IEntityInfo } from "./common/interfaces"; import { ConcurrencyHandler } from "./dal/concurrencyHandler"; import { isMultifileEnabled } from "./utilities/commonUtil"; +import { TreeWebViewProvider } from "./webViews/TreeWebViewProvider"; +import { UserDataMap } from "./context/userDataMap"; export interface IWebExtensionContext { // From portalSchema properties @@ -56,6 +58,8 @@ export interface IWebExtensionContext { defaultFileUri: vscode.Uri; // This will default to home page or current page in multifile scenario showMultifileInVSCode: boolean; extensionActivationTime: number; + treeWebView: TreeWebViewProvider; + containerId: string; // Org specific details dataverseAccessToken: string; @@ -81,6 +85,7 @@ class WebExtensionContext implements IWebExtensionContext { private _vscodeWorkspaceState: Map; private _languageIdCodeMap: Map; private _portalLanguageIdCodeMap: Map; + private _sharedWorkSpaceMap: Map; private _websiteLanguageIdToPortalLanguageMap: Map; private _websiteIdToLanguage: Map; private _rootDirectory: vscode.Uri; @@ -90,6 +95,9 @@ class WebExtensionContext implements IWebExtensionContext { private _defaultFileUri: vscode.Uri; private _showMultifileInVSCode: boolean; private _extensionActivationTime: number; + private _treeWebView: TreeWebViewProvider; + private _containerId: string; + private _connectedUsers: UserDataMap; private _dataverseAccessToken: string; private _entityDataMap: EntityDataMap; private _isContextSet: boolean; @@ -113,6 +121,9 @@ class WebExtensionContext implements IWebExtensionContext { public get urlParametersMap() { return this._urlParametersMap; } + public get sharedWorkSpaceMap() { + return this._sharedWorkSpaceMap; + } public get vscodeWorkspaceState() { return this._vscodeWorkspaceState; } @@ -146,6 +157,21 @@ class WebExtensionContext implements IWebExtensionContext { public get showMultifileInVSCode() { return this._showMultifileInVSCode; } + public get treeWebView() { + return this._treeWebView; + } + public set treeWebView(treeWebView: TreeWebViewProvider) { + this._treeWebView = treeWebView; + } + public get containerId() { + return this._containerId; + } + public set containerId(containerId: string) { + this._containerId = containerId; + } + public get connectedUsers() { + return this._connectedUsers; + } public get extensionActivationTime() { return this._extensionActivationTime } @@ -188,6 +214,7 @@ class WebExtensionContext implements IWebExtensionContext { this._websiteLanguageIdToPortalLanguageMap = new Map(); this._websiteIdToLanguage = new Map(); this._urlParametersMap = new Map(); + this._sharedWorkSpaceMap = new Map(); this._vscodeWorkspaceState = new Map(); this._entitiesFolderNameMap = new Map(); this._defaultEntityType = ""; @@ -199,6 +226,9 @@ class WebExtensionContext implements IWebExtensionContext { this._defaultFileUri = vscode.Uri.parse(``); this._showMultifileInVSCode = false; this._extensionActivationTime = new Date().getTime(); + this._treeWebView = new TreeWebViewProvider(); + this._containerId = ""; + this._connectedUsers = new UserDataMap(); this._isContextSet = false; this._currentSchemaVersion = ""; this._websiteLanguageCode = ""; @@ -323,6 +353,14 @@ class WebExtensionContext implements IWebExtensionContext { } this._dataverseAccessToken = accessToken; + + const websiteid = this.urlParametersMap.get( + Constants.queryParameters.WEBSITE_ID + ) as string; + + const headers = getCommonHeaders(accessToken); + + await this.populateSharedworkspace(headers, dataverseOrgUrl, websiteid); } public async updateFileDetailsInContext( @@ -564,6 +602,49 @@ class WebExtensionContext implements IWebExtensionContext { } } + private async populateSharedworkspace( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + headers: any, + dataverseOrgUrl: string, + websiteid: string + ) { + const sharedworkspace = await getOrCreateSharedWorkspace({ + headers, + dataverseOrgUrl, + websiteid, + }); + + const sharedWorkSpaceParamsMap = new Map(); + for (const key in sharedworkspace) { + sharedWorkSpaceParamsMap.set( + String(key).trim().toLocaleLowerCase(), + String(sharedworkspace[key]).trim() + ); + } + + this._sharedWorkSpaceMap = sharedWorkSpaceParamsMap; + } + + public async updateConnectedUsersInContext( + containerId: string, + fileName: string, + filePath: string, + userName: string, + userId: string + ) { + this.connectedUsers.setUserData( + containerId, + fileName, + filePath, + userName, + userId + ); + } + + public async removeConnectedUserInContext(userId: string) { + this.connectedUsers.removeUser(userId); + } + private async setWebsiteLanguageCode() { const lcid = this.websiteIdToLanguage.get( @@ -598,7 +679,7 @@ class WebExtensionContext implements IWebExtensionContext { } /** - * Store a value maintained in Extension context workspaceState. + * Store a value maintained in Extension context workspaceState. * * *Note* that using `undefined` as value removes the key from the underlying * storage. diff --git a/src/web/client/utilities/urlBuilderUtil.ts b/src/web/client/utilities/urlBuilderUtil.ts index acbcd80c..6ae175e7 100644 --- a/src/web/client/utilities/urlBuilderUtil.ts +++ b/src/web/client/utilities/urlBuilderUtil.ts @@ -17,6 +17,8 @@ import { schemaKey, } from "../schema/constants"; import { getAttributePath, getEntity, getEntityFetchQuery } from "./schemaHelperUtil"; +import { getWorkSpaceName } from "./commonUtil"; +import { v4 as uuidv4 } from 'uuid' export const getParameterizedRequestUrlTemplate = ( useSingleEntityUrl: boolean @@ -242,4 +244,42 @@ export function isWebFileWithLazyLoad(fsPath: string): boolean { fsPath.includes(WebExtensionContext.rootDirectory.fsPath) && fsPath.includes(SCHEMA_WEBFILE_FOLDER_NAME) && !isPreloadedContent; -} \ No newline at end of file +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export async function getOrCreateSharedWorkspace(config: any) { + const getWorkspaceResponse = await fetch( + `${config.dataverseOrgUrl}/api/data/v9.2/sharedworkspaces`, + { + headers: config.headers, + method: "GET", + } + ); + + const getWorkspaceResult = await getWorkspaceResponse.json(); + + if (getWorkspaceResult.value.length) { + for (const workspace of await getWorkspaceResult.value) { + if (workspace.name === getWorkSpaceName(config.websiteid)) { + return workspace; + } + } + } + + const createWorkspaceResponse = await fetch( + `${config.dataverseOrgUrl}/api/data/v9.2/sharedworkspaces`, + { + headers: { + ...config.headers, + Prefer: "return=representation", + }, + method: "POST", + body: JSON.stringify({ + name: getWorkSpaceName(config.websiteid), + sharedworkspaceid: uuidv4(), + }), + } + ); + + return await createWorkspaceResponse.json(); +} diff --git a/src/web/client/webViews/TreeWebViewProvider.ts b/src/web/client/webViews/TreeWebViewProvider.ts new file mode 100644 index 00000000..29fed1bb --- /dev/null +++ b/src/web/client/webViews/TreeWebViewProvider.ts @@ -0,0 +1,88 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import * as vscode from "vscode"; +import WebExtensionContext from "../WebExtensionContext"; + +export class TreeWebViewProvider + implements vscode.TreeDataProvider, vscode.Disposable +{ + private _onDidChangeTreeData: vscode.EventEmitter< + UserNode | undefined | null | void + > = new vscode.EventEmitter(); + + readonly onDidChangeTreeData: vscode.Event< + UserNode | undefined | null | void + > = this._onDidChangeTreeData.event; + + private readonly _disposables: vscode.Disposable[] = []; + + constructor() { + this._disposables.push(...this.registerPanel()); + } + + public dispose(): void { + this._disposables.forEach((d) => d.dispose()); + } + + public getTreeItem( + element: UserNode + ): vscode.TreeItem | Thenable { + const user = new UserNode( + String(element.label), + String(element.description), + element.collapsibleState + ); + + user.tooltip = String(element.label); + + return user; + } + + getChildren(element?: UserNode): vscode.ProviderResult { + if (element) { + return []; + } else { + const connectedUsersMap = + WebExtensionContext.connectedUsers.getUserMap; + const connectedUsers: UserNode[] = Array.from( + connectedUsersMap.entries() + ).map(([, value]) => { + return new UserNode( + value._userName, + value._fileName, + vscode.TreeItemCollapsibleState.None + ); + }); + + return connectedUsers; + } + } + + refresh(): void { + this._onDidChangeTreeData.fire(); + } + + private registerPanel(): vscode.Disposable[] { + return [ + vscode.window.registerTreeDataProvider( + "powerpages.treeWebView", + this + ), + ]; + } +} + +class UserNode extends vscode.TreeItem { + constructor( + label: string | vscode.TreeItemLabel, + fileName: string, + collapsibleState?: vscode.TreeItemCollapsibleState + ) { + super(label, collapsibleState); + this.tooltip = String(fileName); + this.description = fileName; + } +} From 0b706116fc0a6025f37aae508b6607bcc8cd6f67 Mon Sep 17 00:00:00 2001 From: ramukaritik Date: Wed, 20 Sep 2023 03:58:45 +0530 Subject: [PATCH 4/4] Revert "Treeview logic" This reverts commit e7e565da95e66d581b05685f790f4eb6cce4cc85. --- src/web/client/WebExtensionContext.ts | 85 +----------------- src/web/client/utilities/urlBuilderUtil.ts | 42 +-------- .../client/webViews/TreeWebViewProvider.ts | 88 ------------------- 3 files changed, 3 insertions(+), 212 deletions(-) delete mode 100644 src/web/client/webViews/TreeWebViewProvider.ts diff --git a/src/web/client/WebExtensionContext.ts b/src/web/client/WebExtensionContext.ts index 909093ee..6d975b01 100644 --- a/src/web/client/WebExtensionContext.ts +++ b/src/web/client/WebExtensionContext.ts @@ -21,7 +21,7 @@ import { getWebsiteIdToLcidMap, getWebsiteLanguageIdToPortalLanguageIdMap, } from "./utilities/schemaHelperUtil"; -import { getCustomRequestURL, getOrCreateSharedWorkspace } from "./utilities/urlBuilderUtil"; +import { getCustomRequestURL } from "./utilities/urlBuilderUtil"; import { schemaKey } from "./schema/constants"; import { telemetryEventNames } from "./telemetry/constants"; import { EntityDataMap } from "./context/entityDataMap"; @@ -29,8 +29,6 @@ import { FileDataMap } from "./context/fileDataMap"; import { IAttributePath, IEntityInfo } from "./common/interfaces"; import { ConcurrencyHandler } from "./dal/concurrencyHandler"; import { isMultifileEnabled } from "./utilities/commonUtil"; -import { TreeWebViewProvider } from "./webViews/TreeWebViewProvider"; -import { UserDataMap } from "./context/userDataMap"; export interface IWebExtensionContext { // From portalSchema properties @@ -58,8 +56,6 @@ export interface IWebExtensionContext { defaultFileUri: vscode.Uri; // This will default to home page or current page in multifile scenario showMultifileInVSCode: boolean; extensionActivationTime: number; - treeWebView: TreeWebViewProvider; - containerId: string; // Org specific details dataverseAccessToken: string; @@ -85,7 +81,6 @@ class WebExtensionContext implements IWebExtensionContext { private _vscodeWorkspaceState: Map; private _languageIdCodeMap: Map; private _portalLanguageIdCodeMap: Map; - private _sharedWorkSpaceMap: Map; private _websiteLanguageIdToPortalLanguageMap: Map; private _websiteIdToLanguage: Map; private _rootDirectory: vscode.Uri; @@ -95,9 +90,6 @@ class WebExtensionContext implements IWebExtensionContext { private _defaultFileUri: vscode.Uri; private _showMultifileInVSCode: boolean; private _extensionActivationTime: number; - private _treeWebView: TreeWebViewProvider; - private _containerId: string; - private _connectedUsers: UserDataMap; private _dataverseAccessToken: string; private _entityDataMap: EntityDataMap; private _isContextSet: boolean; @@ -121,9 +113,6 @@ class WebExtensionContext implements IWebExtensionContext { public get urlParametersMap() { return this._urlParametersMap; } - public get sharedWorkSpaceMap() { - return this._sharedWorkSpaceMap; - } public get vscodeWorkspaceState() { return this._vscodeWorkspaceState; } @@ -157,21 +146,6 @@ class WebExtensionContext implements IWebExtensionContext { public get showMultifileInVSCode() { return this._showMultifileInVSCode; } - public get treeWebView() { - return this._treeWebView; - } - public set treeWebView(treeWebView: TreeWebViewProvider) { - this._treeWebView = treeWebView; - } - public get containerId() { - return this._containerId; - } - public set containerId(containerId: string) { - this._containerId = containerId; - } - public get connectedUsers() { - return this._connectedUsers; - } public get extensionActivationTime() { return this._extensionActivationTime } @@ -214,7 +188,6 @@ class WebExtensionContext implements IWebExtensionContext { this._websiteLanguageIdToPortalLanguageMap = new Map(); this._websiteIdToLanguage = new Map(); this._urlParametersMap = new Map(); - this._sharedWorkSpaceMap = new Map(); this._vscodeWorkspaceState = new Map(); this._entitiesFolderNameMap = new Map(); this._defaultEntityType = ""; @@ -226,9 +199,6 @@ class WebExtensionContext implements IWebExtensionContext { this._defaultFileUri = vscode.Uri.parse(``); this._showMultifileInVSCode = false; this._extensionActivationTime = new Date().getTime(); - this._treeWebView = new TreeWebViewProvider(); - this._containerId = ""; - this._connectedUsers = new UserDataMap(); this._isContextSet = false; this._currentSchemaVersion = ""; this._websiteLanguageCode = ""; @@ -353,14 +323,6 @@ class WebExtensionContext implements IWebExtensionContext { } this._dataverseAccessToken = accessToken; - - const websiteid = this.urlParametersMap.get( - Constants.queryParameters.WEBSITE_ID - ) as string; - - const headers = getCommonHeaders(accessToken); - - await this.populateSharedworkspace(headers, dataverseOrgUrl, websiteid); } public async updateFileDetailsInContext( @@ -602,49 +564,6 @@ class WebExtensionContext implements IWebExtensionContext { } } - private async populateSharedworkspace( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - headers: any, - dataverseOrgUrl: string, - websiteid: string - ) { - const sharedworkspace = await getOrCreateSharedWorkspace({ - headers, - dataverseOrgUrl, - websiteid, - }); - - const sharedWorkSpaceParamsMap = new Map(); - for (const key in sharedworkspace) { - sharedWorkSpaceParamsMap.set( - String(key).trim().toLocaleLowerCase(), - String(sharedworkspace[key]).trim() - ); - } - - this._sharedWorkSpaceMap = sharedWorkSpaceParamsMap; - } - - public async updateConnectedUsersInContext( - containerId: string, - fileName: string, - filePath: string, - userName: string, - userId: string - ) { - this.connectedUsers.setUserData( - containerId, - fileName, - filePath, - userName, - userId - ); - } - - public async removeConnectedUserInContext(userId: string) { - this.connectedUsers.removeUser(userId); - } - private async setWebsiteLanguageCode() { const lcid = this.websiteIdToLanguage.get( @@ -679,7 +598,7 @@ class WebExtensionContext implements IWebExtensionContext { } /** - * Store a value maintained in Extension context workspaceState. + * Store a value maintained in Extension context workspaceState. * * *Note* that using `undefined` as value removes the key from the underlying * storage. diff --git a/src/web/client/utilities/urlBuilderUtil.ts b/src/web/client/utilities/urlBuilderUtil.ts index 6ae175e7..acbcd80c 100644 --- a/src/web/client/utilities/urlBuilderUtil.ts +++ b/src/web/client/utilities/urlBuilderUtil.ts @@ -17,8 +17,6 @@ import { schemaKey, } from "../schema/constants"; import { getAttributePath, getEntity, getEntityFetchQuery } from "./schemaHelperUtil"; -import { getWorkSpaceName } from "./commonUtil"; -import { v4 as uuidv4 } from 'uuid' export const getParameterizedRequestUrlTemplate = ( useSingleEntityUrl: boolean @@ -244,42 +242,4 @@ export function isWebFileWithLazyLoad(fsPath: string): boolean { fsPath.includes(WebExtensionContext.rootDirectory.fsPath) && fsPath.includes(SCHEMA_WEBFILE_FOLDER_NAME) && !isPreloadedContent; -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export async function getOrCreateSharedWorkspace(config: any) { - const getWorkspaceResponse = await fetch( - `${config.dataverseOrgUrl}/api/data/v9.2/sharedworkspaces`, - { - headers: config.headers, - method: "GET", - } - ); - - const getWorkspaceResult = await getWorkspaceResponse.json(); - - if (getWorkspaceResult.value.length) { - for (const workspace of await getWorkspaceResult.value) { - if (workspace.name === getWorkSpaceName(config.websiteid)) { - return workspace; - } - } - } - - const createWorkspaceResponse = await fetch( - `${config.dataverseOrgUrl}/api/data/v9.2/sharedworkspaces`, - { - headers: { - ...config.headers, - Prefer: "return=representation", - }, - method: "POST", - body: JSON.stringify({ - name: getWorkSpaceName(config.websiteid), - sharedworkspaceid: uuidv4(), - }), - } - ); - - return await createWorkspaceResponse.json(); -} +} \ No newline at end of file diff --git a/src/web/client/webViews/TreeWebViewProvider.ts b/src/web/client/webViews/TreeWebViewProvider.ts deleted file mode 100644 index 29fed1bb..00000000 --- a/src/web/client/webViews/TreeWebViewProvider.ts +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - */ - -import * as vscode from "vscode"; -import WebExtensionContext from "../WebExtensionContext"; - -export class TreeWebViewProvider - implements vscode.TreeDataProvider, vscode.Disposable -{ - private _onDidChangeTreeData: vscode.EventEmitter< - UserNode | undefined | null | void - > = new vscode.EventEmitter(); - - readonly onDidChangeTreeData: vscode.Event< - UserNode | undefined | null | void - > = this._onDidChangeTreeData.event; - - private readonly _disposables: vscode.Disposable[] = []; - - constructor() { - this._disposables.push(...this.registerPanel()); - } - - public dispose(): void { - this._disposables.forEach((d) => d.dispose()); - } - - public getTreeItem( - element: UserNode - ): vscode.TreeItem | Thenable { - const user = new UserNode( - String(element.label), - String(element.description), - element.collapsibleState - ); - - user.tooltip = String(element.label); - - return user; - } - - getChildren(element?: UserNode): vscode.ProviderResult { - if (element) { - return []; - } else { - const connectedUsersMap = - WebExtensionContext.connectedUsers.getUserMap; - const connectedUsers: UserNode[] = Array.from( - connectedUsersMap.entries() - ).map(([, value]) => { - return new UserNode( - value._userName, - value._fileName, - vscode.TreeItemCollapsibleState.None - ); - }); - - return connectedUsers; - } - } - - refresh(): void { - this._onDidChangeTreeData.fire(); - } - - private registerPanel(): vscode.Disposable[] { - return [ - vscode.window.registerTreeDataProvider( - "powerpages.treeWebView", - this - ), - ]; - } -} - -class UserNode extends vscode.TreeItem { - constructor( - label: string | vscode.TreeItemLabel, - fileName: string, - collapsibleState?: vscode.TreeItemCollapsibleState - ) { - super(label, collapsibleState); - this.tooltip = String(fileName); - this.description = fileName; - } -}