mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-14 01:25:37 -05:00
253 lines
9.7 KiB
TypeScript
253 lines
9.7 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* 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 azdataType from 'azdata';
|
|
import * as vscode from 'vscode';
|
|
import { DialogBase } from './dialogBase';
|
|
import * as constants from '../common/constants';
|
|
import { IWorkspaceService } from '../common/interfaces';
|
|
import { directoryExist, fileExist } from '../common/utils';
|
|
import { IconPathHelper } from '../common/iconHelper';
|
|
import { TelemetryActions, TelemetryReporter, TelemetryViews } from '../common/telemetry';
|
|
import { defaultProjectSaveLocation } from '../common/projectLocationHelper';
|
|
|
|
export class OpenExistingDialog extends DialogBase {
|
|
public filePathTextBox: azdataType.InputBoxComponent | undefined;
|
|
public filePathAndButtonComponent: azdataType.FormComponent | undefined;
|
|
public gitRepoTextBoxComponent: azdataType.FormComponent | undefined;
|
|
public localClonePathComponent: azdataType.FormComponent | undefined;
|
|
public localClonePathTextBox: azdataType.InputBoxComponent | undefined;
|
|
public localRadioButton: azdataType.RadioButtonComponent | undefined;
|
|
public remoteGitRepoRadioButton: azdataType.RadioButtonComponent | undefined;
|
|
public locationRadioButtonFormComponent: azdataType.FormComponent | undefined;
|
|
public formBuilder: azdataType.FormBuilder | undefined;
|
|
|
|
constructor(private workspaceService: IWorkspaceService) {
|
|
super(constants.OpenExistingDialogTitle, 'OpenProject', constants.OpenButtonText);
|
|
|
|
// dialog launched from Welcome message button (only visible when no current workspace) vs. "add project" button
|
|
TelemetryReporter.createActionEvent(TelemetryViews.OpenExistingDialog, TelemetryActions.OpenExistingDialogLaunched)
|
|
.withAdditionalProperties({ isWorkspaceOpen: (vscode.workspace.workspaceFile !== undefined).toString() })
|
|
.send();
|
|
}
|
|
|
|
async validate(): Promise<boolean> {
|
|
try {
|
|
if (this.localRadioButton?.checked) {
|
|
await this.validateFile(this.filePathTextBox!.value!, constants.Project.toLowerCase());
|
|
} else {
|
|
await this.validateClonePath(<string>this.localClonePathTextBox!.value);
|
|
}
|
|
|
|
if (await this.workspaceService.validateWorkspace() === false) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
catch (err) {
|
|
this.showErrorMessage(err?.message ? err.message : err);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public async validateFile(file: string, fileType: string): Promise<void> {
|
|
const fileExists = await fileExist(file);
|
|
if (!fileExists) {
|
|
throw new Error(constants.FileNotExistError(fileType, file));
|
|
}
|
|
}
|
|
|
|
public async validateClonePath(location: string): Promise<void> {
|
|
// only need to check if parent directory exists
|
|
// if the same repo has been cloned before, the git clone will append the next number to the folder
|
|
const parentDirectoryExists = await directoryExist(location);
|
|
if (!parentDirectoryExists) {
|
|
throw new Error(constants.CloneParentDirectoryNotExistError(location));
|
|
}
|
|
}
|
|
|
|
override async onComplete(): Promise<void> {
|
|
try {
|
|
// save datapoint now because it'll get set to new value during validateWorkspace()
|
|
const telemetryProps: any = { hasWorkspaceOpen: (vscode.workspace.workspaceFile !== undefined).toString() };
|
|
|
|
let addProjectsPromise: Promise<void>;
|
|
|
|
if (this.remoteGitRepoRadioButton!.checked) {
|
|
TelemetryReporter.createActionEvent(TelemetryViews.OpenExistingDialog, TelemetryActions.GitClone)
|
|
.withAdditionalProperties({ selectedTarget: 'project' })
|
|
.send();
|
|
|
|
addProjectsPromise = this.workspaceService.gitCloneProject((<azdataType.InputBoxComponent>this.gitRepoTextBoxComponent?.component).value!, this.localClonePathTextBox!.value!);
|
|
} else {
|
|
telemetryProps.cancelled = 'false';
|
|
addProjectsPromise = this.workspaceService.addProjectsToWorkspace([vscode.Uri.file(this.filePathTextBox!.value!)]);
|
|
}
|
|
|
|
TelemetryReporter.createActionEvent(TelemetryViews.OpenExistingDialog, TelemetryActions.OpeningProject)
|
|
.withAdditionalProperties(telemetryProps)
|
|
.send();
|
|
|
|
await addProjectsPromise;
|
|
}
|
|
catch (err) {
|
|
void vscode.window.showErrorMessage(err?.message ? err.message : err);
|
|
}
|
|
}
|
|
|
|
protected async initialize(view: azdataType.ModelView): Promise<void> {
|
|
this.localRadioButton = view.modelBuilder.radioButton().withProps({
|
|
name: 'location',
|
|
label: constants.Local,
|
|
checked: true
|
|
}).component();
|
|
|
|
this.register(this.localRadioButton.onDidChangeCheckedState(checked => {
|
|
if (checked) {
|
|
this.formBuilder?.removeFormItem(<azdataType.FormComponent>this.gitRepoTextBoxComponent);
|
|
this.formBuilder?.removeFormItem(<azdataType.FormComponent>this.localClonePathComponent);
|
|
this.formBuilder?.insertFormItem(<azdataType.FormComponent>this.filePathAndButtonComponent, 1);
|
|
}
|
|
}));
|
|
|
|
this.remoteGitRepoRadioButton = view.modelBuilder.radioButton().withProps({
|
|
name: 'location',
|
|
label: constants.RemoteGitRepo
|
|
}).component();
|
|
|
|
this.locationRadioButtonFormComponent = {
|
|
title: constants.LocationSelectorTitle,
|
|
required: true,
|
|
component: view.modelBuilder.flexContainer()
|
|
.withItems([this.localRadioButton, this.remoteGitRepoRadioButton], { flex: '0 0 auto', CSSStyles: { 'margin-right': '15px' } })
|
|
.withProps({ ariaRole: 'radiogroup' })
|
|
.component()
|
|
};
|
|
|
|
this.register(this.remoteGitRepoRadioButton.onDidChangeCheckedState(checked => {
|
|
if (checked) {
|
|
this.formBuilder?.removeFormItem(<azdataType.FormComponent>this.filePathAndButtonComponent);
|
|
this.formBuilder?.insertFormItem(<azdataType.FormComponent>this.gitRepoTextBoxComponent, 1);
|
|
this.formBuilder?.insertFormItem(<azdataType.FormComponent>this.localClonePathComponent, 2);
|
|
}
|
|
}));
|
|
|
|
const gitRepoTextBox = view.modelBuilder.inputBox().withProps({
|
|
ariaLabel: constants.GitRepoUrlTitle,
|
|
placeHolder: constants.GitRepoUrlPlaceholder,
|
|
required: true,
|
|
width: constants.DefaultInputWidth
|
|
}).component();
|
|
|
|
this.register(gitRepoTextBox.onTextChanged(() => {
|
|
return gitRepoTextBox.updateProperty('title', this.localClonePathTextBox!.value!);
|
|
}));
|
|
|
|
this.gitRepoTextBoxComponent = {
|
|
title: constants.GitRepoUrlTitle,
|
|
component: gitRepoTextBox
|
|
};
|
|
|
|
this.localClonePathTextBox = view.modelBuilder.inputBox().withProps({
|
|
ariaLabel: constants.LocalClonePathTitle,
|
|
placeHolder: constants.LocalClonePathPlaceholder,
|
|
required: true,
|
|
width: constants.DefaultInputWidth
|
|
}).component();
|
|
|
|
this.register(this.localClonePathTextBox.onTextChanged(() => {
|
|
return this.localClonePathTextBox!.updateProperty('title', this.localClonePathTextBox!.value!);
|
|
}));
|
|
|
|
const localClonePathBrowseFolderButton = view.modelBuilder.button().withProps({
|
|
ariaLabel: constants.BrowseButtonText,
|
|
iconPath: IconPathHelper.folder,
|
|
width: '18px',
|
|
height: '16px',
|
|
}).component();
|
|
|
|
this.register(localClonePathBrowseFolderButton.onDidClick(async () => {
|
|
const folderUris = await vscode.window.showOpenDialog({
|
|
canSelectFiles: false,
|
|
canSelectFolders: true,
|
|
canSelectMany: false,
|
|
defaultUri: defaultProjectSaveLocation()
|
|
});
|
|
if (!folderUris || folderUris.length === 0) {
|
|
return;
|
|
}
|
|
|
|
const selectedFolder = folderUris[0].fsPath;
|
|
this.localClonePathTextBox!.value = selectedFolder;
|
|
void this.localClonePathTextBox!.updateProperty('title', this.localClonePathTextBox!.value);
|
|
}));
|
|
|
|
this.localClonePathComponent = {
|
|
title: constants.LocalClonePathTitle,
|
|
component: this.createHorizontalContainer(view, [this.localClonePathTextBox, localClonePathBrowseFolderButton]),
|
|
required: true
|
|
};
|
|
|
|
this.filePathTextBox = view.modelBuilder.inputBox().withProps({
|
|
ariaLabel: constants.LocationSelectorTitle,
|
|
placeHolder: constants.ProjectFilePlaceholder,
|
|
required: true,
|
|
width: constants.DefaultInputWidth
|
|
}).component();
|
|
|
|
this.register(this.filePathTextBox.onTextChanged(() => {
|
|
return this.filePathTextBox!.updateProperty('title', this.filePathTextBox!.value!);
|
|
}));
|
|
|
|
const localProjectBrowseFolderButton = view.modelBuilder.button().withProps({
|
|
ariaLabel: constants.BrowseButtonText,
|
|
iconPath: IconPathHelper.folder,
|
|
width: '18px',
|
|
height: '16px'
|
|
}).component();
|
|
this.register(localProjectBrowseFolderButton.onDidClick(() => this.onBrowseButtonClick()));
|
|
|
|
const flexContainer = this.createHorizontalContainer(view, [this.filePathTextBox, localProjectBrowseFolderButton]);
|
|
void flexContainer.updateCssStyles({ 'margin-top': '-10px' });
|
|
this.filePathAndButtonComponent = {
|
|
component: flexContainer
|
|
};
|
|
|
|
this.formBuilder = view.modelBuilder.formContainer().withFormItems([
|
|
this.locationRadioButtonFormComponent,
|
|
this.filePathAndButtonComponent,
|
|
]);
|
|
await view.initializeModel(this.formBuilder?.component());
|
|
this.initDialogComplete?.resolve();
|
|
}
|
|
|
|
public async onBrowseButtonClick(): Promise<void> {
|
|
const projectFilePath = await browseForProject(this.workspaceService);
|
|
if (projectFilePath) {
|
|
this.filePathTextBox!.value = projectFilePath.fsPath;
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function browseForProject(workspaceService: IWorkspaceService): Promise<vscode.Uri | undefined> {
|
|
const filters: { [name: string]: string[] } = {};
|
|
const projectTypes = await workspaceService.getAllProjectTypes();
|
|
filters[constants.AllProjectTypes] = [...new Set(projectTypes.map(type => type.projectFileExtension))];
|
|
projectTypes.forEach(type => {
|
|
filters[type.displayName] = [type.projectFileExtension];
|
|
});
|
|
|
|
const fileUris = await vscode.window.showOpenDialog({
|
|
canSelectFiles: true,
|
|
canSelectFolders: false,
|
|
canSelectMany: false,
|
|
openLabel: constants.SelectProjectFileActionName,
|
|
filters: filters,
|
|
defaultUri: defaultProjectSaveLocation()
|
|
});
|
|
|
|
return fileUris?.[0];
|
|
}
|