Add extension API for using the same file browser dialog that's used for Restore (#24151)

Co-authored-by: Charles Gagnon <chgagnon@microsoft.com>
This commit is contained in:
Cory Rivera
2023-08-17 14:39:55 -07:00
committed by GitHub
parent 0e7942913b
commit a2336a2617
11 changed files with 124 additions and 14 deletions

View File

@@ -24,3 +24,4 @@ import './mainThreadResourceProvider';
import './mainThreadErrorDiagnostics';
import './mainThreadTasks';
import './mainThreadWorkspace';
import './mainThreadWindow';

View File

@@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import type * as azdata from 'azdata';
import { MainThreadWindowShape } from 'sql/workbench/api/common/sqlExtHost.protocol';
import { IFileBrowserDialogController } from 'sql/workbench/services/fileBrowser/common/fileBrowserDialogController';
import { Disposable } from 'vs/base/common/lifecycle';
import { SqlMainContext } from 'vs/workbench/api/common/extHost.protocol';
import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers';
@extHostNamedCustomer(SqlMainContext.MainThreadWindow)
export class MainThreadWindow extends Disposable implements MainThreadWindowShape {
constructor(
extHostContext: IExtHostContext,
@IFileBrowserDialogController private _fileBrowserDialogService: IFileBrowserDialogController
) {
super();
}
public async $openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Promise<string | undefined> {
let completion = new Promise<string | undefined>((resolve, reject) => {
try {
const handleOnClosed = (path: string | undefined) => {
resolve(path);
};
this._fileBrowserDialogService.showDialog(connectionUri, targetPath, fileFilters, '', true, handleOnClosed);
} catch (error) {
reject(error);
}
});
return await completion;
}
}

View File

@@ -0,0 +1,23 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import type * as azdata from 'azdata';
import { IMainContext } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostWindowShape, MainThreadWindowShape } from 'sql/workbench/api/common/sqlExtHost.protocol';
import { SqlMainContext } from 'vs/workbench/api/common/extHost.protocol';
export class ExtHostWindow implements ExtHostWindowShape {
private readonly _proxy: MainThreadWindowShape;
constructor(_mainContext: IMainContext) {
this._proxy = _mainContext.getProxy(SqlMainContext.MainThreadWindow);
}
$openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Promise<string | undefined> {
return this._proxy.$openServerFileBrowserDialog(connectionUri, targetPath, fileFilters);
}
}

View File

@@ -42,6 +42,7 @@ import { ExtHostAzureBlob } from 'sql/workbench/api/common/extHostAzureBlob';
import { ExtHostAzureAccount } from 'sql/workbench/api/common/extHostAzureAccount';
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { AuthenticationType } from 'sql/platform/connection/common/constants';
import { ExtHostWindow } from 'sql/workbench/api/common/extHostWindow';
export interface IAzdataExtensionApiFactory {
(extension: IExtensionDescription): typeof azdata;
@@ -102,6 +103,7 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
const extHostNotebook = rpcProtocol.set(SqlExtHostContext.ExtHostNotebook, new ExtHostNotebook(rpcProtocol));
const extHostExtensionManagement = rpcProtocol.set(SqlExtHostContext.ExtHostExtensionManagement, new ExtHostExtensionManagement(rpcProtocol));
const extHostWorkspace = rpcProtocol.set(SqlExtHostContext.ExtHostWorkspace, new ExtHostWorkspace(rpcProtocol));
const extHostWindow = rpcProtocol.set(SqlExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol));
return {
azdata: function (extension: IExtensionDescription): typeof azdata {
// namespace: connection
@@ -480,6 +482,9 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
MessageLevel: sqlExtHostTypes.MessageLevel,
openCustomErrorDialog(options: sqlExtHostTypes.IErrorDialogOptions): Thenable<string | undefined> {
return extHostModelViewDialog.openCustomErrorDialog(options);
},
openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Thenable<string | undefined> {
return extHostWindow.$openServerFileBrowserDialog(connectionUri, targetPath, fileFilters);
}
};

View File

@@ -827,12 +827,20 @@ export interface ExtHostWorkspaceShape {
$saveAndEnterWorkspace(workspaceFile: vscode.Uri): Promise<void>;
}
export interface ExtHostWindowShape {
$openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Promise<string | undefined>;
}
export interface MainThreadWorkspaceShape {
$createAndEnterWorkspace(folder: vscode.Uri, workspaceFile: vscode.Uri): Promise<void>;
$enterWorkspace(workspaceFile: vscode.Uri): Promise<void>;
$saveAndEnterWorkspace(workspaceFile: vscode.Uri): Promise<void>;
}
export interface MainThreadWindowShape {
$openServerFileBrowserDialog(connectionUri: string, targetPath: string, fileFilters: azdata.window.FileFilters[]): Promise<string | undefined>;
}
export interface MainThreadBackgroundTaskManagementShape extends IDisposable {
$registerTask(taskInfo: azdata.TaskInfo): void;
$updateTask(taskProgressInfo: azdata.TaskProgressInfo): void;

View File

@@ -766,7 +766,7 @@ export class BackupComponent extends AngularDisposable {
}
}
private handleFilePathAdded(filepath: string): void {
private handleFilePathAdded(filepath?: string): void {
if (filepath && !this.backupPathTypePairs![filepath]) {
if ((this.getBackupPathCount() < BackupConstants.maxDevices)) {
this.backupPathTypePairs![filepath] = BackupConstants.MediaDeviceType.File;

View File

@@ -25,7 +25,7 @@ export class FileBrowserDialogController implements IFileBrowserDialogController
fileFilters: [{ label: string, filters: string[] }],
fileValidationServiceType: string,
isWide: boolean,
handleOnOk: (path: string) => void
handleOnClosed: (path: string | undefined) => void
): void {
if (!this._fileBrowserDialog) {
this._fileBrowserDialog = this._instantiationService.createInstance(FileBrowserDialog, localize('filebrowser.selectFile', "Select a file"));
@@ -33,7 +33,17 @@ export class FileBrowserDialogController implements IFileBrowserDialogController
}
this._fileBrowserDialog.setWide(isWide);
this._fileBrowserDialog.onOk((filepath) => handleOnOk(filepath));
var onOK = this._fileBrowserDialog.onOk((filepath) => handleOnClosed(filepath));
var onClosed = this._fileBrowserDialog.onClosed((hideReason) => {
if (hideReason !== 'ok') {
handleOnClosed(undefined);
}
onOK.dispose();
onClosed.dispose();
this._fileBrowserDialog.dispose();
this._fileBrowserDialog = undefined;
});
this._fileBrowserDialog.open(ownerUri, expandPath, fileFilters, fileValidationServiceType);
}
}

View File

@@ -16,5 +16,5 @@ export interface IFileBrowserDialogController {
fileFilters: { label: string, filters: string[] }[],
fileValidationServiceType: string,
isWide: boolean,
handleOnOk: (path: string) => void): void;
handleOnOk: (path: string | undefined) => void): void;
}

View File

@@ -725,16 +725,18 @@ export class RestoreDialog extends Modal {
.then(url => this._urlInputBox!.value = url);
}
private onFileBrowsed(filepath: string): void {
const oldFilePath = this._filePathInputBox!.value;
if (strings.isFalsyOrWhitespace(this._filePathInputBox!.value)) {
this._filePathInputBox!.value = filepath;
} else {
this._filePathInputBox!.value = this._filePathInputBox!.value + ', ' + filepath;
}
private onFileBrowsed(filepath?: string): void {
if (filepath) {
const oldFilePath = this._filePathInputBox!.value;
if (strings.isFalsyOrWhitespace(this._filePathInputBox!.value)) {
this._filePathInputBox!.value = filepath;
} else {
this._filePathInputBox!.value = this._filePathInputBox!.value + ', ' + filepath;
}
if (oldFilePath !== this._filePathInputBox!.value) {
this.onFilePathChanged(this._filePathInputBox!.value);
if (oldFilePath !== this._filePathInputBox!.value) {
this.onFilePathChanged(this._filePathInputBox!.value);
}
}
}