From 08c4ffadd66ad3b655bd3eec2e75b2acdd1fee54 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 28 Nov 2022 20:29:32 +0100 Subject: [PATCH] recommend remote extension (#167419) * recommend remote extension * some fixes --- .../remoteTunnel/common/remoteTunnel.ts | 2 + .../electron-browser/remoteTunnelService.ts | 8 ++- .../remoteTunnel.contribution.ts | 72 ++++++++++++++++++- 3 files changed, 79 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/remoteTunnel/common/remoteTunnel.ts b/src/vs/platform/remoteTunnel/common/remoteTunnel.ts index 16a7aa1e56d..831686e6123 100644 --- a/src/vs/platform/remoteTunnel/common/remoteTunnel.ts +++ b/src/vs/platform/remoteTunnel/common/remoteTunnel.ts @@ -25,6 +25,8 @@ export interface IRemoteTunnelService { readonly onDidChangeAccount: Event; updateAccount(account: IRemoteTunnelAccount | undefined): Promise; + getHostName(): Promise; + } export type TunnelStatus = TunnelStates.Connected | TunnelStates.Disconnected | TunnelStates.Connecting | TunnelStates.Uninitialized; diff --git a/src/vs/platform/remoteTunnel/electron-browser/remoteTunnelService.ts b/src/vs/platform/remoteTunnel/electron-browser/remoteTunnelService.ts index 3fb8c1c812c..34ae4a7790a 100644 --- a/src/vs/platform/remoteTunnel/electron-browser/remoteTunnelService.ts +++ b/src/vs/platform/remoteTunnel/electron-browser/remoteTunnelService.ts @@ -171,7 +171,7 @@ export class RemoteTunnelService extends Disposable implements IRemoteTunnelServ return; } - const hostName = this.getHostName(); + const hostName = this._getHostName(); if (hostName) { this.setTunnelStatus(TunnelStates.connecting(localize({ key: 'remoteTunnelService.openTunnelWithName', comment: ['{0} is a host name'] }, 'Opening tunnel for {0}', hostName))); } else { @@ -279,7 +279,11 @@ export class RemoteTunnelService extends Disposable implements IRemoteTunnelServ }); } - private getHostName() { + public async getHostName(): Promise { + return this._getHostName(); + } + + private _getHostName(): string | undefined { let name = this.configurationService.getValue(CONFIGURATION_KEY_HOST_NAME) || hostname(); name = name.replace(/[^\w-]/g, '').substring(0, 20); return name || undefined; diff --git a/src/vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution.ts b/src/vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution.ts index 9fb30ca3f45..01694760a2e 100644 --- a/src/vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution.ts +++ b/src/vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution.ts @@ -51,6 +51,9 @@ export const REMOTE_TUNNEL_CONNECTION_STATE = new RawContextKey { + if (this.storageService.getBoolean(REMOTE_TUNNEL_EXTENSION_RECOMMENDED_KEY, StorageScope.APPLICATION)) { + return false; + } + if (await this.extensionService.getExtension(remoteExtension.extensionId)) { + return false; + } + const usedOnHost = this.storageService.get(REMOTE_TUNNEL_USED_STORAGE_KEY, StorageScope.APPLICATION); + if (!usedOnHost) { + return false; + } + const currentHostName = await this.remoteTunnelService.getHostName(); + if (!currentHostName || currentHostName === usedOnHost) { + return false; + } + return usedOnHost; + }; + const recommed = async () => { + const usedOnHost = await shouldRecommend(); + if (!usedOnHost) { + return false; + } + this.notificationService.notify({ + severity: Severity.Info, + message: + localize( + { + key: 'recommend.remoteExtension', + comment: ['{0} will be a host name, {1} will the link address to the web UI, {6} an extension name. [label](command:commandId) is a markdown link. Only translate the label, do not modify the format'] + }, + "'{0}' has turned on remote access. The {1} extension can be used to connect to it.", + usedOnHost, remoteExtension.friendlyName + ), + actions: { + primary: [ + new Action('showExtension', localize('action.showExtension', "Show Extension"), undefined, true, () => { + return this.commandService.executeCommand('workbench.extensions.action.showExtensionsWithIds', [remoteExtension.extensionId]); + }), + new Action('doNotShowAgain', localize('action.doNotShowAgain', "Do not show again"), undefined, true, () => { + this.storageService.store(REMOTE_TUNNEL_EXTENSION_RECOMMENDED_KEY, true, StorageScope.APPLICATION, StorageTarget.USER); + }), + ] + } + }); + return true; + }; + if (await shouldRecommend()) { + const storageListener = this.storageService.onDidChangeValue(async e => { + if (e.key === REMOTE_TUNNEL_USED_STORAGE_KEY) { + const success = await recommed(); + if (success) { + storageListener.dispose(); + } + + } + }); + } + } + + private async initialize(): Promise { const status = await this.remoteTunnelService.getTunnelStatus(); if (status.type === 'connected') { @@ -418,6 +486,7 @@ export class RemoteTunnelWorkbenchContribution extends Disposable implements IWo const notificationService = accessor.get(INotificationService); const clipboardService = accessor.get(IClipboardService); const commandService = accessor.get(ICommandService); + const storageService = accessor.get(IStorageService); const connectionInfo = await that.startTunnel(false); if (connectionInfo) { @@ -443,6 +512,7 @@ export class RemoteTunnelWorkbenchContribution extends Disposable implements IWo ] } }); + storageService.store(REMOTE_TUNNEL_USED_STORAGE_KEY, connectionInfo.hostName, StorageScope.APPLICATION, StorageTarget.USER); } else { await notificationService.notify({ severity: Severity.Info, @@ -667,7 +737,7 @@ Registry.as(ConfigurationExtensions.Configuration).regis [CONFIGURATION_KEY_HOST_NAME]: { description: localize('remoteTunnelAccess.machineName', "The name under which the remote tunnel access is registered. If not set, the host name is used."), type: 'string', - scope: ConfigurationScope.APPLICATION, + scope: ConfigurationScope.MACHINE, pattern: '^[\\w-]*$', patternErrorMessage: localize('remoteTunnelAccess.machineNameRegex', "The name can only consist of letters, numbers, underscore and minus."), maxLength: 20,