mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
More Layering (#9139)
* move handling generated files to the serilization classes * remove unneeded methods * add more folders to strictire compile, add more strict compile options * update ci * wip * add more layering and fix issues * add more strictness * remove unnecessary assertion * add missing checks * fix indentation * wip * remove jsdoc * fix layering * fix compile * fix compile errors * wip * wip * finish layering * fix css * more layering * rip * reworking results serializer * move some files around * move capabilities to platform wip * implement capabilities register provider * fix capabilities service * fix usage of the regist4ry * add contribution * wip * wip * wip * remove no longer good parts * fix strict-nulls * fix issues with startup * another try * fix startup * fix imports * fix tests * fix tests * fix more tests * fix tests * fix more tests * fix broken test * fix tabbing * fix naming * wip * finished layering * fix imports * fix valid layers * fix layers
This commit is contained in:
@@ -0,0 +1,367 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/accountDialog';
|
||||
import 'vs/css!./media/accountActions';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { SplitView, Sizing } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { values } from 'vs/base/common/map';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
import { Button } from 'sql/base/browser/ui/button/button';
|
||||
import { Modal } from 'sql/workbench/browser/modal/modal';
|
||||
import { attachButtonStyler } from 'sql/platform/theme/common/styler';
|
||||
import { AccountViewModel } from 'sql/platform/accounts/common/accountViewModel';
|
||||
import { AddAccountAction } from 'sql/platform/accounts/common/accountActions';
|
||||
import { AccountListRenderer, AccountListDelegate } from 'sql/workbench/services/accountManagement/browser/accountListRenderer';
|
||||
import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/platform/accounts/common/eventTypes';
|
||||
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
||||
import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { attachModalDialogStyler, attachPanelStyler } from 'sql/workbench/common/styler';
|
||||
import { IViewDescriptorService } from 'vs/workbench/common/views';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
|
||||
class AccountPanel extends ViewPane {
|
||||
public index: number;
|
||||
private accountList: List<azdata.Account>;
|
||||
|
||||
constructor(
|
||||
private options: IViewPaneOptions,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
|
||||
@IOpenerService openerService: IOpenerService,
|
||||
) {
|
||||
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService);
|
||||
}
|
||||
|
||||
protected renderBody(container: HTMLElement): void {
|
||||
this.accountList = new List<azdata.Account>('AccountList', container, new AccountListDelegate(AccountDialog.ACCOUNTLIST_HEIGHT), [this.instantiationService.createInstance(AccountListRenderer)]);
|
||||
this._register(attachListStyler(this.accountList, this.themeService));
|
||||
}
|
||||
|
||||
protected layoutBody(size: number): void {
|
||||
if (this.accountList) {
|
||||
this.accountList.layout(size);
|
||||
}
|
||||
}
|
||||
|
||||
public get length(): number {
|
||||
return this.accountList.length;
|
||||
}
|
||||
|
||||
public focus() {
|
||||
this.accountList.domFocus();
|
||||
}
|
||||
|
||||
public updateAccounts(accounts: azdata.Account[]) {
|
||||
this.accountList.splice(0, this.accountList.length, accounts);
|
||||
}
|
||||
|
||||
public setSelection(indexes: number[]) {
|
||||
this.accountList.setSelection(indexes);
|
||||
}
|
||||
|
||||
public getActions(): IAction[] {
|
||||
return [this.instantiationService.createInstance(
|
||||
AddAccountAction,
|
||||
this.options.id
|
||||
)];
|
||||
}
|
||||
}
|
||||
|
||||
export interface IProviderViewUiComponent {
|
||||
view: AccountPanel;
|
||||
addAccountAction: AddAccountAction;
|
||||
}
|
||||
|
||||
export class AccountDialog extends Modal {
|
||||
public static ACCOUNTLIST_HEIGHT = 77;
|
||||
|
||||
public viewModel: AccountViewModel;
|
||||
|
||||
// MEMBER VARIABLES ////////////////////////////////////////////////////
|
||||
private _providerViewsMap = new Map<string, IProviderViewUiComponent>();
|
||||
|
||||
private _closeButton: Button;
|
||||
private _addAccountButton: Button;
|
||||
private _splitView: SplitView;
|
||||
private _container: HTMLElement;
|
||||
private _splitViewContainer: HTMLElement;
|
||||
private _noaccountViewContainer: HTMLElement;
|
||||
|
||||
// EVENTING ////////////////////////////////////////////////////////////
|
||||
private _onAddAccountErrorEmitter: Emitter<string>;
|
||||
public get onAddAccountErrorEvent(): Event<string> { return this._onAddAccountErrorEmitter.event; }
|
||||
|
||||
private _onCloseEmitter: Emitter<void>;
|
||||
public get onCloseEvent(): Event<void> { return this._onCloseEmitter.event; }
|
||||
|
||||
constructor(
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IContextMenuService private _contextMenuService: IContextMenuService,
|
||||
@IKeybindingService private _keybindingService: IKeybindingService,
|
||||
@IConfigurationService private _configurationService: IConfigurationService,
|
||||
@IAdsTelemetryService telemetryService: IAdsTelemetryService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IClipboardService clipboardService: IClipboardService,
|
||||
@ILogService logService: ILogService,
|
||||
@IViewDescriptorService private viewDescriptorService: IViewDescriptorService,
|
||||
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService,
|
||||
@IOpenerService protected readonly openerService: IOpenerService
|
||||
) {
|
||||
super(
|
||||
localize('linkedAccounts', "Linked accounts"),
|
||||
TelemetryKeys.Accounts,
|
||||
telemetryService,
|
||||
layoutService,
|
||||
clipboardService,
|
||||
themeService,
|
||||
logService,
|
||||
textResourcePropertiesService,
|
||||
contextKeyService,
|
||||
{ hasSpinner: true }
|
||||
);
|
||||
|
||||
// Setup the event emitters
|
||||
this._onAddAccountErrorEmitter = new Emitter<string>();
|
||||
this._onCloseEmitter = new Emitter<void>();
|
||||
|
||||
// Create the view model and wire up the events
|
||||
this.viewModel = this._instantiationService.createInstance(AccountViewModel);
|
||||
this.viewModel.addProviderEvent(arg => { this.addProvider(arg); });
|
||||
this.viewModel.removeProviderEvent(arg => { this.removeProvider(arg); });
|
||||
this.viewModel.updateAccountListEvent(arg => { this.updateProviderAccounts(arg); });
|
||||
|
||||
// Load the initial contents of the view model
|
||||
this.viewModel.initialize()
|
||||
.then(addedProviders => {
|
||||
for (const addedProvider of addedProviders) {
|
||||
this.addProvider(addedProvider);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// MODAL OVERRIDE METHODS //////////////////////////////////////////////
|
||||
protected layout(height?: number): void {
|
||||
this._splitView.layout(DOM.getContentHeight(this._container));
|
||||
}
|
||||
|
||||
public render() {
|
||||
super.render();
|
||||
attachModalDialogStyler(this, this._themeService);
|
||||
this._closeButton = this.addFooterButton(localize('accountDialog.close', "Close"), () => this.close());
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
protected renderBody(container: HTMLElement) {
|
||||
this._container = container;
|
||||
this._splitViewContainer = DOM.$('div.account-view.monaco-pane-view');
|
||||
DOM.append(container, this._splitViewContainer);
|
||||
this._splitView = new SplitView(this._splitViewContainer);
|
||||
|
||||
this._noaccountViewContainer = DOM.$('div.no-account-view');
|
||||
const noAccountTitle = DOM.append(this._noaccountViewContainer, DOM.$('.no-account-view-label'));
|
||||
const noAccountLabel = localize('accountDialog.noAccountLabel', "There is no linked account. Please add an account.");
|
||||
noAccountTitle.innerText = noAccountLabel;
|
||||
|
||||
// Show the add account button for the first provider
|
||||
// Todo: If we have more than 1 provider, need to show all add account buttons for all providers
|
||||
const buttonSection = DOM.append(this._noaccountViewContainer, DOM.$('div.button-section'));
|
||||
this._addAccountButton = new Button(buttonSection);
|
||||
this._addAccountButton.label = localize('accountDialog.addConnection', "Add an account");
|
||||
|
||||
this._register(this._addAccountButton.onDidClick(() => {
|
||||
(<IProviderViewUiComponent>values(this._providerViewsMap)[0]).addAccountAction.run();
|
||||
}));
|
||||
|
||||
DOM.append(container, this._noaccountViewContainer);
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
// Theme styler
|
||||
this._register(attachButtonStyler(this._closeButton, this._themeService));
|
||||
this._register(attachButtonStyler(this._addAccountButton, this._themeService));
|
||||
}
|
||||
|
||||
/* Overwrite escape key behavior */
|
||||
protected onClose() {
|
||||
this.close();
|
||||
}
|
||||
|
||||
/* Overwrite enter key behavior */
|
||||
protected onAccept() {
|
||||
this.close();
|
||||
}
|
||||
|
||||
public close() {
|
||||
this._onCloseEmitter.fire();
|
||||
this.hide();
|
||||
}
|
||||
|
||||
public open() {
|
||||
this.show();
|
||||
if (!this.isEmptyLinkedAccount()) {
|
||||
this.showSplitView();
|
||||
} else {
|
||||
this.showNoAccountContainer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private showNoAccountContainer() {
|
||||
this._splitViewContainer.hidden = true;
|
||||
this._noaccountViewContainer.hidden = false;
|
||||
this._addAccountButton.focus();
|
||||
}
|
||||
|
||||
private showSplitView() {
|
||||
this._splitViewContainer.hidden = false;
|
||||
this._noaccountViewContainer.hidden = true;
|
||||
if (values(this._providerViewsMap).length > 0) {
|
||||
const firstView = values(this._providerViewsMap)[0];
|
||||
if (firstView instanceof AccountPanel) {
|
||||
firstView.setSelection([0]);
|
||||
firstView.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private isEmptyLinkedAccount(): boolean {
|
||||
for (const provider of values(this._providerViewsMap)) {
|
||||
const listView = provider.view;
|
||||
if (listView && listView.length > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
for (const provider of values(this._providerViewsMap)) {
|
||||
if (provider.addAccountAction) {
|
||||
provider.addAccountAction.dispose();
|
||||
}
|
||||
if (provider.view) {
|
||||
provider.view.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PRIVATE HELPERS /////////////////////////////////////////////////////
|
||||
private addProvider(newProvider: AccountProviderAddedEventParams) {
|
||||
|
||||
// Skip adding the provider if it already exists
|
||||
if (this._providerViewsMap.get(newProvider.addedProvider.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Account provider doesn't exist, so add it
|
||||
// Create a scoped add account action
|
||||
let addAccountAction = this._instantiationService.createInstance(
|
||||
AddAccountAction,
|
||||
newProvider.addedProvider.id
|
||||
);
|
||||
addAccountAction.addAccountCompleteEvent(() => this.spinner = false);
|
||||
addAccountAction.addAccountErrorEvent(msg => this._onAddAccountErrorEmitter.fire(msg));
|
||||
addAccountAction.addAccountStartEvent(() => this.spinner = true);
|
||||
|
||||
let providerView = new AccountPanel(
|
||||
{
|
||||
id: newProvider.addedProvider.id,
|
||||
title: newProvider.addedProvider.displayName,
|
||||
ariaHeaderLabel: newProvider.addedProvider.displayName
|
||||
},
|
||||
this._keybindingService,
|
||||
this._contextMenuService,
|
||||
this._configurationService,
|
||||
this._themeService,
|
||||
this.contextKeyService,
|
||||
this._instantiationService,
|
||||
this.viewDescriptorService,
|
||||
this.openerService
|
||||
);
|
||||
|
||||
attachPanelStyler(providerView, this._themeService);
|
||||
|
||||
const insertIndex = this._splitView.length;
|
||||
// Append the list view to the split view
|
||||
this._splitView.addView(providerView, Sizing.Distribute, insertIndex);
|
||||
providerView.render();
|
||||
providerView.index = insertIndex;
|
||||
|
||||
this._splitView.layout(DOM.getContentHeight(this._container));
|
||||
|
||||
// Set the initial items of the list
|
||||
providerView.updateAccounts(newProvider.initialAccounts);
|
||||
|
||||
if (newProvider.initialAccounts.length > 0 && this._splitViewContainer.hidden) {
|
||||
this.showSplitView();
|
||||
}
|
||||
|
||||
this.layout();
|
||||
|
||||
// Store the view for the provider and action
|
||||
this._providerViewsMap.set(newProvider.addedProvider.id, { view: providerView, addAccountAction: addAccountAction });
|
||||
}
|
||||
|
||||
private removeProvider(removedProvider: azdata.AccountProviderMetadata) {
|
||||
// Skip removing the provider if it doesn't exist
|
||||
const providerView = this._providerViewsMap.get(removedProvider.id);
|
||||
if (!providerView || !providerView.view) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the list view from the split view
|
||||
this._splitView.removeView(providerView.view.index);
|
||||
this._splitView.layout(DOM.getContentHeight(this._container));
|
||||
|
||||
// Remove the list view from our internal map
|
||||
this._providerViewsMap.delete(removedProvider.id);
|
||||
this.layout();
|
||||
}
|
||||
|
||||
private updateProviderAccounts(args: UpdateAccountListEventParams) {
|
||||
const providerMapping = this._providerViewsMap.get(args.providerId);
|
||||
if (!providerMapping || !providerMapping.view) {
|
||||
return;
|
||||
}
|
||||
providerMapping.view.updateAccounts(args.accountList);
|
||||
|
||||
if (args.accountList.length > 0 && this._splitViewContainer.hidden) {
|
||||
this.showSplitView();
|
||||
}
|
||||
|
||||
if (this.isEmptyLinkedAccount() && this._noaccountViewContainer.hidden) {
|
||||
this.showNoAccountContainer();
|
||||
}
|
||||
|
||||
this.layout();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { AccountDialog } from 'sql/workbench/services/accountManagement/browser/accountDialog';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
|
||||
|
||||
export class AccountDialogController {
|
||||
|
||||
// MEMBER VARIABLES ////////////////////////////////////////////////////
|
||||
private _addAccountErrorTitle = localize('accountDialog.addAccountErrorTitle', "Error adding account");
|
||||
|
||||
private _accountDialog: AccountDialog;
|
||||
public get accountDialog(): AccountDialog { return this._accountDialog; }
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IErrorMessageService private _errorMessageService: IErrorMessageService
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Open account dialog
|
||||
*/
|
||||
public openAccountDialog(): void {
|
||||
// Create a new dialog if one doesn't exist
|
||||
if (!this._accountDialog) {
|
||||
this._accountDialog = this._instantiationService.createInstance(AccountDialog);
|
||||
this._accountDialog.onAddAccountErrorEvent(msg => this.handleOnAddAccountError(msg));
|
||||
this._accountDialog.onCloseEvent(() => this.handleOnClose());
|
||||
this._accountDialog.render();
|
||||
}
|
||||
|
||||
// Open the dialog
|
||||
this._accountDialog.open();
|
||||
}
|
||||
|
||||
// PRIVATE HELPERS /////////////////////////////////////////////////////
|
||||
private handleOnClose(): void { }
|
||||
|
||||
private handleOnAddAccountError(msg: string): void {
|
||||
this._errorMessageService.showDialog(Severity.Error, this._addAccountErrorTitle, msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/accountListRenderer';
|
||||
import 'vs/css!./media/accountActions';
|
||||
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { ActionBar, IActionOptions } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
import { RemoveAccountAction, RefreshAccountAction } from 'sql/platform/accounts/common/accountActions';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
export class AccountListDelegate implements IListVirtualDelegate<azdata.Account> {
|
||||
|
||||
constructor(
|
||||
private _height: number
|
||||
) {
|
||||
}
|
||||
|
||||
public getHeight(element: azdata.Account): number {
|
||||
return this._height;
|
||||
}
|
||||
|
||||
public getTemplateId(element: azdata.Account): string {
|
||||
return 'accountListRenderer';
|
||||
}
|
||||
}
|
||||
|
||||
export interface AccountPickerListTemplate {
|
||||
root: HTMLElement;
|
||||
icon: HTMLElement;
|
||||
badgeContent: HTMLElement;
|
||||
contextualDisplayName: HTMLElement;
|
||||
label: HTMLElement;
|
||||
displayName: HTMLElement;
|
||||
}
|
||||
|
||||
export interface AccountListTemplate extends AccountPickerListTemplate {
|
||||
content: HTMLElement;
|
||||
actions: ActionBar;
|
||||
}
|
||||
|
||||
export class AccountPickerListRenderer implements IListRenderer<azdata.Account, AccountPickerListTemplate> {
|
||||
public static TEMPLATE_ID = 'accountListRenderer';
|
||||
|
||||
public get templateId(): string {
|
||||
return AccountPickerListRenderer.TEMPLATE_ID;
|
||||
}
|
||||
|
||||
public renderTemplate(container: HTMLElement): AccountPickerListTemplate {
|
||||
const tableTemplate: AccountPickerListTemplate = Object.create(null);
|
||||
const badge = DOM.$('div.badge');
|
||||
tableTemplate.root = DOM.append(container, DOM.$('div.list-row.account-picker-list'));
|
||||
tableTemplate.icon = DOM.append(tableTemplate.root, DOM.$('div.codicon'));
|
||||
DOM.append(tableTemplate.icon, badge);
|
||||
tableTemplate.badgeContent = DOM.append(badge, DOM.$('div.badge-content'));
|
||||
tableTemplate.label = DOM.append(tableTemplate.root, DOM.$('div.label'));
|
||||
tableTemplate.contextualDisplayName = DOM.append(tableTemplate.label, DOM.$('div.contextual-display-name'));
|
||||
tableTemplate.displayName = DOM.append(tableTemplate.label, DOM.$('div.display-name'));
|
||||
return tableTemplate;
|
||||
}
|
||||
|
||||
public renderElement(account: azdata.Account, index: number, templateData: AccountPickerListTemplate): void {
|
||||
// Set the account icon
|
||||
templateData.icon.classList.add('account-logo', account.displayInfo.accountType);
|
||||
|
||||
templateData.contextualDisplayName.innerText = account.displayInfo.contextualDisplayName;
|
||||
|
||||
// show the account display name text, something like "User Name (user@email.com)"
|
||||
if (account.displayInfo.userId && account.displayInfo.displayName) {
|
||||
templateData.displayName.innerText = account.displayInfo.displayName + ' (' + account.displayInfo.userId + ')';
|
||||
} else {
|
||||
templateData.displayName.innerText = account.displayInfo.displayName;
|
||||
}
|
||||
|
||||
if (account.isStale) {
|
||||
templateData.badgeContent.className = 'badge-content codicon warning-badge';
|
||||
} else {
|
||||
templateData.badgeContent.className = 'badge-content';
|
||||
}
|
||||
}
|
||||
|
||||
public disposeTemplate(template: AccountPickerListTemplate): void {
|
||||
// noop
|
||||
}
|
||||
|
||||
public disposeElement(element: azdata.Account, index: number, templateData: AccountPickerListTemplate): void {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
export class AccountListRenderer extends AccountPickerListRenderer {
|
||||
constructor(
|
||||
@IInstantiationService private _instantiationService: IInstantiationService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
public get templateId(): string {
|
||||
return AccountListRenderer.TEMPLATE_ID;
|
||||
}
|
||||
|
||||
public renderTemplate(container: HTMLElement): AccountListTemplate {
|
||||
const tableTemplate = super.renderTemplate(container) as AccountListTemplate;
|
||||
tableTemplate.content = DOM.append(tableTemplate.label, DOM.$('div.content'));
|
||||
tableTemplate.actions = new ActionBar(tableTemplate.root, { animated: false });
|
||||
|
||||
return tableTemplate;
|
||||
}
|
||||
|
||||
public renderElement(account: azdata.Account, index: number, templateData: AccountListTemplate): void {
|
||||
super.renderElement(account, index, templateData);
|
||||
if (account.isStale) {
|
||||
templateData.content.innerText = localize('refreshCredentials', "You need to refresh the credentials for this account.");
|
||||
} else {
|
||||
templateData.content.innerText = '';
|
||||
}
|
||||
templateData.actions.clear();
|
||||
|
||||
const actionOptions: IActionOptions = { icon: true, label: false };
|
||||
if (account.isStale) {
|
||||
const refreshAction = this._instantiationService.createInstance(RefreshAccountAction);
|
||||
refreshAction.account = account;
|
||||
templateData.actions.push(refreshAction, actionOptions);
|
||||
} else {
|
||||
// Todo: Will show filter action when API/GUI for filtering is implemented (#3022, #3024)
|
||||
// templateData.actions.push(new ApplyFilterAction(ApplyFilterAction.ID, ApplyFilterAction.LABEL), actionOptions);
|
||||
}
|
||||
|
||||
const removeAction = this._instantiationService.createInstance(RemoveAccountAction, account);
|
||||
templateData.actions.push(removeAction, actionOptions);
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,8 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag
|
||||
import { Memento } from 'vs/workbench/common/memento';
|
||||
|
||||
import AccountStore from 'sql/platform/accounts/common/accountStore';
|
||||
import { AccountDialogController } from 'sql/workbench/contrib/accounts/browser/accountDialogController';
|
||||
import { AutoOAuthDialogController } from 'sql/workbench/contrib/accounts/browser/autoOAuthDialogController';
|
||||
import { AccountDialogController } from 'sql/workbench/services/accountManagement/browser/accountDialogController';
|
||||
import { AutoOAuthDialogController } from 'sql/workbench/services/accountManagement/browser/autoOAuthDialogController';
|
||||
import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/platform/accounts/common/eventTypes';
|
||||
import { IAccountManagementService } from 'sql/platform/accounts/common/interfaces';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
export const IAccountPickerService = createDecorator<IAccountPickerService>('AccountPickerService');
|
||||
export interface IAccountPickerService {
|
||||
_serviceBrand: undefined;
|
||||
renderAccountPicker(container: HTMLElement): void;
|
||||
addAccountCompleteEvent: Event<void>;
|
||||
addAccountErrorEvent: Event<string>;
|
||||
addAccountStartEvent: Event<void>;
|
||||
onAccountSelectionChangeEvent: Event<azdata.Account | undefined>;
|
||||
selectedAccount: azdata.Account | undefined;
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/accountPicker';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { IDropdownOptions } from 'vs/base/browser/ui/dropdown/dropdown';
|
||||
import { IListEvent } from 'vs/base/browser/ui/list/list';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { buttonBackground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import { DropdownList } from 'sql/base/browser/ui/dropdownList/dropdownList';
|
||||
import { attachDropdownStyler } from 'sql/platform/theme/common/styler';
|
||||
import { AddAccountAction, RefreshAccountAction } from 'sql/platform/accounts/common/accountActions';
|
||||
import { AccountPickerListRenderer, AccountListDelegate } from 'sql/workbench/services/accountManagement/browser/accountListRenderer';
|
||||
import { AccountPickerViewModel } from 'sql/platform/accounts/common/accountPickerViewModel';
|
||||
import { firstIndex } from 'vs/base/common/arrays';
|
||||
|
||||
export class AccountPicker extends Disposable {
|
||||
public static ACCOUNTPICKERLIST_HEIGHT = 47;
|
||||
public viewModel: AccountPickerViewModel;
|
||||
private _accountList: List<azdata.Account>;
|
||||
private _rootElement: HTMLElement;
|
||||
private _refreshContainer: HTMLElement;
|
||||
private _listContainer: HTMLElement;
|
||||
private _dropdown: DropdownList;
|
||||
private _refreshAccountAction: RefreshAccountAction;
|
||||
|
||||
// EVENTING ////////////////////////////////////////////////////////////
|
||||
private _addAccountCompleteEmitter: Emitter<void>;
|
||||
public get addAccountCompleteEvent(): Event<void> { return this._addAccountCompleteEmitter.event; }
|
||||
|
||||
private _addAccountErrorEmitter: Emitter<string>;
|
||||
public get addAccountErrorEvent(): Event<string> { return this._addAccountErrorEmitter.event; }
|
||||
|
||||
private _addAccountStartEmitter: Emitter<void>;
|
||||
public get addAccountStartEvent(): Event<void> { return this._addAccountStartEmitter.event; }
|
||||
|
||||
private _onAccountSelectionChangeEvent: Emitter<azdata.Account | undefined>;
|
||||
public get onAccountSelectionChangeEvent(): Event<azdata.Account | undefined> { return this._onAccountSelectionChangeEvent.event; }
|
||||
|
||||
constructor(
|
||||
private _providerId: string,
|
||||
@IThemeService private _themeService: IThemeService,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IContextViewService private _contextViewService: IContextViewService
|
||||
) {
|
||||
super();
|
||||
|
||||
// Create event emitters
|
||||
this._addAccountCompleteEmitter = new Emitter<void>();
|
||||
this._addAccountErrorEmitter = new Emitter<string>();
|
||||
this._addAccountStartEmitter = new Emitter<void>();
|
||||
this._onAccountSelectionChangeEvent = new Emitter<azdata.Account>();
|
||||
|
||||
// Create the view model, wire up the events, and initialize with baseline data
|
||||
this.viewModel = this._instantiationService.createInstance(AccountPickerViewModel, this._providerId);
|
||||
this.viewModel.updateAccountListEvent(arg => {
|
||||
if (arg.providerId === this._providerId) {
|
||||
this.updateAccountList(arg.accountList);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// PUBLIC METHODS //////////////////////////////////////////////////////
|
||||
/**
|
||||
* Render account picker
|
||||
*/
|
||||
public render(container: HTMLElement): void {
|
||||
DOM.append(container, this._rootElement);
|
||||
}
|
||||
|
||||
// PUBLIC METHODS //////////////////////////////////////////////////////
|
||||
/**
|
||||
* Create account picker component
|
||||
*/
|
||||
public createAccountPickerComponent() {
|
||||
// Create an account list
|
||||
const delegate = new AccountListDelegate(AccountPicker.ACCOUNTPICKERLIST_HEIGHT);
|
||||
const accountRenderer = new AccountPickerListRenderer();
|
||||
this._listContainer = DOM.$('div.account-list-container');
|
||||
this._accountList = new List<azdata.Account>('AccountPicker', this._listContainer, delegate, [accountRenderer]);
|
||||
this._register(attachListStyler(this._accountList, this._themeService));
|
||||
|
||||
this._rootElement = DOM.$('div.account-picker-container');
|
||||
|
||||
// Create a dropdown for account picker
|
||||
const option: IDropdownOptions = {
|
||||
contextViewProvider: this._contextViewService,
|
||||
labelRenderer: (container) => this.renderLabel(container)
|
||||
};
|
||||
|
||||
// Create the add account action
|
||||
const addAccountAction = this._instantiationService.createInstance(AddAccountAction, this._providerId);
|
||||
addAccountAction.addAccountCompleteEvent(() => this._addAccountCompleteEmitter.fire());
|
||||
addAccountAction.addAccountErrorEvent((msg) => this._addAccountErrorEmitter.fire(msg));
|
||||
addAccountAction.addAccountStartEvent(() => this._addAccountStartEmitter.fire());
|
||||
|
||||
this._dropdown = this._register(new DropdownList(this._rootElement, option, this._listContainer, this._accountList, addAccountAction));
|
||||
this._register(attachDropdownStyler(this._dropdown, this._themeService));
|
||||
this._register(this._accountList.onSelectionChange((e: IListEvent<azdata.Account>) => {
|
||||
if (e.elements.length === 1) {
|
||||
this._dropdown.renderLabel();
|
||||
this.onAccountSelectionChange(e.elements[0]);
|
||||
}
|
||||
}));
|
||||
|
||||
// Create refresh account action
|
||||
this._refreshContainer = DOM.append(this._rootElement, DOM.$('div.refresh-container'));
|
||||
DOM.append(this._refreshContainer, DOM.$('div.sql codicon warning'));
|
||||
const actionBar = new ActionBar(this._refreshContainer, { animated: false });
|
||||
this._refreshAccountAction = this._instantiationService.createInstance(RefreshAccountAction);
|
||||
actionBar.push(this._refreshAccountAction, { icon: false, label: true });
|
||||
|
||||
if (this._accountList.length > 0) {
|
||||
this._accountList.setSelection([0]);
|
||||
this.onAccountSelectionChange(this._accountList.getSelectedElements()[0]);
|
||||
} else {
|
||||
DOM.hide(this._refreshContainer);
|
||||
}
|
||||
|
||||
this._register(this._themeService.onThemeChange(e => this.updateTheme(e)));
|
||||
this.updateTheme(this._themeService.getTheme());
|
||||
|
||||
// Load the initial contents of the view model
|
||||
this.viewModel.initialize()
|
||||
.then((accounts: azdata.Account[]) => {
|
||||
this.updateAccountList(accounts);
|
||||
});
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
super.dispose();
|
||||
if (this._accountList) {
|
||||
this._accountList.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// PRIVATE HELPERS /////////////////////////////////////////////////////
|
||||
private onAccountSelectionChange(account: azdata.Account | undefined) {
|
||||
this.viewModel.selectedAccount = account;
|
||||
if (account && account.isStale) {
|
||||
this._refreshAccountAction.account = account;
|
||||
DOM.show(this._refreshContainer);
|
||||
} else {
|
||||
DOM.hide(this._refreshContainer);
|
||||
}
|
||||
|
||||
this._onAccountSelectionChangeEvent.fire(account);
|
||||
}
|
||||
|
||||
private renderLabel(container: HTMLElement): IDisposable | null {
|
||||
if (container.hasChildNodes()) {
|
||||
for (let i = 0; i < container.childNodes.length; i++) {
|
||||
container.removeChild(container.childNodes.item(i));
|
||||
}
|
||||
}
|
||||
|
||||
const selectedAccounts = this._accountList.getSelectedElements();
|
||||
const account = selectedAccounts ? selectedAccounts[0] : undefined;
|
||||
if (account) {
|
||||
const badge = DOM.$('div.badge');
|
||||
const row = DOM.append(container, DOM.$('div.selected-account-container'));
|
||||
const icon = DOM.append(row, DOM.$('div.codicon'));
|
||||
DOM.append(icon, badge);
|
||||
const badgeContent = DOM.append(badge, DOM.$('div.badge-content'));
|
||||
const label = DOM.append(row, DOM.$('div.label'));
|
||||
|
||||
// Set the account icon
|
||||
icon.classList.add('icon', account.displayInfo.accountType);
|
||||
|
||||
// TODO: Pick between the light and dark logo
|
||||
label.innerText = account.displayInfo.displayName + ' (' + account.displayInfo.contextualDisplayName + ')';
|
||||
|
||||
if (account.isStale) {
|
||||
badgeContent.className = 'badge-content codicon warning-badge';
|
||||
} else {
|
||||
badgeContent.className = 'badge-content';
|
||||
}
|
||||
} else {
|
||||
const row = DOM.append(container, DOM.$('div.no-account-container'));
|
||||
row.innerText = AddAccountAction.LABEL + '...';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private updateAccountList(accounts: azdata.Account[]): void {
|
||||
// keep the selection to the current one
|
||||
const selectedElements = this._accountList.getSelectedElements();
|
||||
|
||||
// find selected index
|
||||
let selectedIndex: number | undefined;
|
||||
if (selectedElements.length > 0 && accounts.length > 0) {
|
||||
selectedIndex = firstIndex(accounts, (account) => {
|
||||
return (account.key.accountId === selectedElements[0].key.accountId);
|
||||
});
|
||||
}
|
||||
|
||||
// Replace the existing list with the new one
|
||||
this._accountList.splice(0, this._accountList.length, accounts);
|
||||
|
||||
if (this._accountList.length > 0) {
|
||||
if (selectedIndex && selectedIndex !== -1) {
|
||||
this._accountList.setSelection([selectedIndex]);
|
||||
} else {
|
||||
this._accountList.setSelection([0]);
|
||||
}
|
||||
} else {
|
||||
// if the account is empty, re-render dropdown label
|
||||
this.onAccountSelectionChange(undefined);
|
||||
this._dropdown.renderLabel();
|
||||
}
|
||||
|
||||
this._accountList.layout(this._accountList.contentHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update theming that is specific to account picker
|
||||
*/
|
||||
private updateTheme(theme: ITheme): void {
|
||||
const linkColor = theme.getColor(buttonBackground);
|
||||
const link = linkColor ? linkColor.toString() : null;
|
||||
this._refreshContainer.style.color = link;
|
||||
if (this._refreshContainer) {
|
||||
this._refreshContainer.style.color = link;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
import { IAccountPickerService } from 'sql/workbench/services/accountManagement/browser/accountPicker';
|
||||
import { AccountPicker } from 'sql/workbench/services/accountManagement/browser/accountPickerImpl';
|
||||
|
||||
export class AccountPickerService implements IAccountPickerService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private _accountPicker: AccountPicker;
|
||||
|
||||
// EVENTING ////////////////////////////////////////////////////////////
|
||||
private _addAccountCompleteEmitter: Emitter<void>;
|
||||
public get addAccountCompleteEvent(): Event<void> { return this._addAccountCompleteEmitter.event; }
|
||||
|
||||
private _addAccountErrorEmitter: Emitter<string>;
|
||||
public get addAccountErrorEvent(): Event<string> { return this._addAccountErrorEmitter.event; }
|
||||
|
||||
private _addAccountStartEmitter: Emitter<void>;
|
||||
public get addAccountStartEvent(): Event<void> { return this._addAccountStartEmitter.event; }
|
||||
|
||||
private _onAccountSelectionChangeEvent: Emitter<azdata.Account | undefined>;
|
||||
public get onAccountSelectionChangeEvent(): Event<azdata.Account | undefined> { return this._onAccountSelectionChangeEvent.event; }
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private _instantiationService: IInstantiationService
|
||||
) {
|
||||
// Create event emitters
|
||||
this._addAccountCompleteEmitter = new Emitter<void>();
|
||||
this._addAccountErrorEmitter = new Emitter<string>();
|
||||
this._addAccountStartEmitter = new Emitter<void>();
|
||||
this._onAccountSelectionChangeEvent = new Emitter<azdata.Account>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get selected account
|
||||
*/
|
||||
public get selectedAccount(): azdata.Account | undefined {
|
||||
return this._accountPicker.viewModel.selectedAccount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render account picker
|
||||
*/
|
||||
public renderAccountPicker(container: HTMLElement): void {
|
||||
if (!this._accountPicker) {
|
||||
// TODO: expand support to multiple providers
|
||||
const providerId: string = 'azurePublicCloud';
|
||||
this._accountPicker = this._instantiationService.createInstance(AccountPicker, providerId);
|
||||
this._accountPicker.createAccountPickerComponent();
|
||||
}
|
||||
|
||||
this._accountPicker.addAccountCompleteEvent(() => this._addAccountCompleteEmitter.fire());
|
||||
this._accountPicker.addAccountErrorEvent((msg) => this._addAccountErrorEmitter.fire(msg));
|
||||
this._accountPicker.addAccountStartEvent(() => this._addAccountStartEmitter.fire());
|
||||
this._accountPicker.onAccountSelectionChangeEvent((account) => this._onAccountSelectionChangeEvent.fire(account));
|
||||
this._accountPicker.render(container);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/autoOAuthDialog';
|
||||
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { $, append } from 'vs/base/browser/dom';
|
||||
|
||||
import { Button } from 'sql/base/browser/ui/button/button';
|
||||
import { Modal } from 'sql/workbench/browser/modal/modal';
|
||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
||||
import { attachButtonStyler } from 'sql/platform/theme/common/styler';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
||||
import { attachModalDialogStyler } from 'sql/workbench/common/styler';
|
||||
|
||||
export class AutoOAuthDialog extends Modal {
|
||||
private _copyAndOpenButton: Button;
|
||||
private _closeButton: Button;
|
||||
private _userCodeInputBox: InputBox;
|
||||
private _websiteInputBox: InputBox;
|
||||
private _descriptionElement: HTMLElement;
|
||||
|
||||
// EVENTING ////////////////////////////////////////////////////////////
|
||||
private _onHandleAddAccount = new Emitter<void>();
|
||||
public get onHandleAddAccount(): Event<void> { return this._onHandleAddAccount.event; }
|
||||
|
||||
private _onCancel = new Emitter<void>();
|
||||
public get onCancel(): Event<void> { return this._onCancel.event; }
|
||||
|
||||
|
||||
private _onCloseEvent = new Emitter<void>();
|
||||
public get onCloseEvent(): Event<void> { return this._onCloseEvent.event; }
|
||||
|
||||
constructor(
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextViewService private _contextViewService: IContextViewService,
|
||||
@IAdsTelemetryService telemetryService: IAdsTelemetryService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IClipboardService clipboardService: IClipboardService,
|
||||
@ILogService logService: ILogService,
|
||||
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService
|
||||
) {
|
||||
super(
|
||||
'',
|
||||
TelemetryKeys.AutoOAuth,
|
||||
telemetryService,
|
||||
layoutService,
|
||||
clipboardService,
|
||||
themeService,
|
||||
logService,
|
||||
textResourcePropertiesService,
|
||||
contextKeyService,
|
||||
{
|
||||
isFlyout: true,
|
||||
hasBackButton: true,
|
||||
hasSpinner: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public render() {
|
||||
super.render();
|
||||
attachModalDialogStyler(this, this._themeService);
|
||||
this.backButton.onDidClick(() => this.cancel());
|
||||
this._register(attachButtonStyler(this.backButton, this._themeService, { buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND }));
|
||||
|
||||
this._copyAndOpenButton = this.addFooterButton(localize('copyAndOpen', "Copy & Open"), () => this.addAccount());
|
||||
this._closeButton = this.addFooterButton(localize('oauthDialog.cancel', "Cancel"), () => this.cancel());
|
||||
this.registerListeners();
|
||||
this._userCodeInputBox.disable();
|
||||
this._websiteInputBox.disable();
|
||||
}
|
||||
|
||||
protected layout(height?: number): void {
|
||||
// NO OP
|
||||
}
|
||||
|
||||
protected renderBody(container: HTMLElement) {
|
||||
const body = append(container, $('.auto-oauth-dialog'));
|
||||
this._descriptionElement = append(body, $('.auto-oauth-description-section.new-section'));
|
||||
|
||||
const addAccountSection = append(body, $('.auto-oauth-info-section.new-section'));
|
||||
this._userCodeInputBox = this.createInputBoxHelper(addAccountSection, localize('userCode', "User code"));
|
||||
this._websiteInputBox = this.createInputBoxHelper(addAccountSection, localize('website', "Website"));
|
||||
}
|
||||
|
||||
private createInputBoxHelper(container: HTMLElement, label: string): InputBox {
|
||||
const inputContainer = append(container, $('.dialog-input-section'));
|
||||
append(inputContainer, $('.dialog-label')).innerText = label;
|
||||
const inputCellContainer = append(inputContainer, $('.dialog-input'));
|
||||
|
||||
return new InputBox(inputCellContainer, this._contextViewService, {
|
||||
ariaLabel: label
|
||||
});
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
// Theme styler
|
||||
this._register(attachButtonStyler(this._copyAndOpenButton, this._themeService));
|
||||
this._register(attachButtonStyler(this._closeButton, this._themeService));
|
||||
this._register(attachInputBoxStyler(this._userCodeInputBox, this._themeService));
|
||||
this._register(attachInputBoxStyler(this._websiteInputBox, this._themeService));
|
||||
|
||||
}
|
||||
|
||||
/* Overwrite escape key behavior */
|
||||
protected onClose() {
|
||||
this.cancel();
|
||||
}
|
||||
|
||||
/* Overwrite enter key behavior */
|
||||
protected onAccept() {
|
||||
this.addAccount();
|
||||
}
|
||||
|
||||
private addAccount() {
|
||||
if (this._copyAndOpenButton.enabled) {
|
||||
this._copyAndOpenButton.enabled = false;
|
||||
this.spinner = true;
|
||||
this._onHandleAddAccount.fire();
|
||||
}
|
||||
}
|
||||
|
||||
public cancel() {
|
||||
this._onCancel.fire();
|
||||
}
|
||||
|
||||
public close() {
|
||||
this._copyAndOpenButton.enabled = true;
|
||||
this._onCloseEvent.fire();
|
||||
this.spinner = false;
|
||||
this.hide();
|
||||
}
|
||||
|
||||
public open(title: string, message: string, userCode: string, uri: string) {
|
||||
// Update dialog
|
||||
this.title = title;
|
||||
this._descriptionElement.innerText = message;
|
||||
this._userCodeInputBox.value = userCode;
|
||||
this._websiteInputBox.value = uri;
|
||||
this.show();
|
||||
this._copyAndOpenButton.focus();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
import { AutoOAuthDialog } from 'sql/workbench/services/accountManagement/browser/autoOAuthDialog';
|
||||
import { IAccountManagementService } from 'sql/platform/accounts/common/interfaces';
|
||||
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
|
||||
|
||||
export class AutoOAuthDialogController {
|
||||
// MEMBER VARIABLES ////////////////////////////////////////////////////
|
||||
private _autoOAuthDialog: AutoOAuthDialog;
|
||||
private _providerId?: string;
|
||||
private _userCode: string;
|
||||
private _uri: string;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IAccountManagementService private _accountManagementService: IAccountManagementService,
|
||||
@IErrorMessageService private _errorMessageService: IErrorMessageService
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Open auto OAuth dialog
|
||||
*/
|
||||
public openAutoOAuthDialog(providerId: string, title: string, message: string, userCode: string, uri: string): Thenable<void> {
|
||||
if (this._providerId !== undefined) {
|
||||
// If a oauth flyout is already open, return an error
|
||||
const errorMessage = localize('oauthFlyoutIsAlreadyOpen', "Cannot start auto OAuth. An auto OAuth is already in progress.");
|
||||
this._errorMessageService.showDialog(Severity.Error, '', errorMessage);
|
||||
return Promise.reject(new Error('Auto OAuth dialog already open'));
|
||||
}
|
||||
|
||||
// Create a new dialog if one doesn't exist
|
||||
if (!this._autoOAuthDialog) {
|
||||
this._autoOAuthDialog = this._instantiationService.createInstance(AutoOAuthDialog);
|
||||
this._autoOAuthDialog.onHandleAddAccount(this.handleOnAddAccount, this);
|
||||
this._autoOAuthDialog.onCancel(this.handleOnCancel, this);
|
||||
this._autoOAuthDialog.onCloseEvent(this.handleOnClose, this);
|
||||
this._autoOAuthDialog.render();
|
||||
}
|
||||
|
||||
this._userCode = userCode;
|
||||
this._uri = uri;
|
||||
|
||||
// Open the dialog
|
||||
this._autoOAuthDialog.open(title, message, userCode, uri);
|
||||
this._providerId = providerId;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close auto OAuth dialog
|
||||
*/
|
||||
public closeAutoOAuthDialog(): void {
|
||||
this._autoOAuthDialog.close();
|
||||
this._providerId = undefined;
|
||||
}
|
||||
|
||||
// PRIVATE HELPERS /////////////////////////////////////////////////////
|
||||
private handleOnCancel(): void {
|
||||
this._accountManagementService.cancelAutoOAuthDeviceCode(this._providerId!); // this should be always true
|
||||
}
|
||||
|
||||
private handleOnClose(): void {
|
||||
this._providerId = undefined;
|
||||
}
|
||||
|
||||
private handleOnAddAccount(): void {
|
||||
this._accountManagementService.copyUserCodeAndOpenBrowser(this._userCode, this._uri);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* Icons for various account actions */
|
||||
.vs .action-item .codicon.add-linked-account-action {
|
||||
background-image: url('new_account.svg');
|
||||
}
|
||||
|
||||
.vs-dark .action-item .codicon.add-linked-account-action,
|
||||
.hc-black .action-item .codicon.add-linked-account-action {
|
||||
background-image: url('new_account_inverse.svg');
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.no-account-view {
|
||||
font-size: 12px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.no-account-view .no-account-view-label {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.account-view .header {
|
||||
position: relative;
|
||||
line-height: 22px;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
padding-left: 20px;
|
||||
padding-right: 12px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.account-view .header .title {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.account-view .header .actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.account-view .header .actions .action-item .action-label {
|
||||
width: 30px;
|
||||
background-size: 16px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
margin-right: 0;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.account-view .header .count-badge-wrapper {
|
||||
justify-content: flex-end;
|
||||
/* hide the count badge as it is not providing much value and meanwhile not keyboard accessible*/
|
||||
display: none;
|
||||
}
|
||||
|
||||
.account-view .list-row {
|
||||
padding: 12px;
|
||||
line-height: 18px !important;
|
||||
}
|
||||
|
||||
.account-view .list-row .codicon {
|
||||
flex: 0 0 50px;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
background-size: 50px;
|
||||
}
|
||||
|
||||
.account-view .list-row .codicon .badge {
|
||||
position: absolute;
|
||||
top: 43px;
|
||||
left: 43px;
|
||||
overflow: hidden;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.account-view .list-row .codicon .badge .badge-content {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
background-size: 22px;
|
||||
}
|
||||
|
||||
.account-view .list-row .actions-container {
|
||||
flex: 0 0 50px;
|
||||
}
|
||||
|
||||
.account-view .list-row .actions-container .action-item .action-label {
|
||||
width: 16px;
|
||||
margin-left: 20px;
|
||||
margin-right: 10px;
|
||||
background-size: 16px;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.account-view .list-row .actions-container .action-item .action-label.codicon.remove {
|
||||
background-size: 14px !important;
|
||||
}
|
||||
|
||||
.account-view .list-row .actions-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.account-view .monaco-list .monaco-list-row:hover .list-row .actions-container,
|
||||
.account-view .monaco-list .monaco-list-row.selected .list-row .actions-container,
|
||||
.account-view .monaco-list .monaco-list-row.focused .list-row .actions-container{
|
||||
display: block;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.list-row.account-picker-list {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.list-row.account-picker-list .label {
|
||||
flex: 1 1 auto;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.list-row.account-picker-list .label .contextual-display-name {
|
||||
font-size: 15px;
|
||||
line-height: 15px;
|
||||
}
|
||||
|
||||
.list-row.account-picker-list .label .display-name {
|
||||
font-size: 13px;
|
||||
line-height: 13px;
|
||||
}
|
||||
|
||||
.list-row.account-picker-list .label .content {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.account-logo {
|
||||
background: no-repeat center center;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* Selected account */
|
||||
.selected-account-container {
|
||||
padding: 6px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.selected-account-container .codicon {
|
||||
flex: 0 0 25px;
|
||||
height: 25px;
|
||||
width: 25px;
|
||||
}
|
||||
|
||||
.selected-account-container .label {
|
||||
flex: 1 1 auto;
|
||||
padding-left: 10px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.selected-account-container .codicon {
|
||||
background-size: 25px;
|
||||
}
|
||||
|
||||
.selected-account-container .codicon .badge {
|
||||
position: relative;
|
||||
top: 15px;
|
||||
left: 15px;
|
||||
overflow: hidden;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.selected-account-container .codicon .badge .badge-content {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-size: 12px;
|
||||
}
|
||||
|
||||
/* A container when the account list is empty */
|
||||
.no-account-container {
|
||||
padding: 6px;
|
||||
opacity: 0.7;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* Account list */
|
||||
.account-list-container .list-row {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.account-list-container .list-row .codicon {
|
||||
flex: 0 0 35px;
|
||||
height: 35px;
|
||||
width: 35px;
|
||||
background-size: 35px;
|
||||
}
|
||||
|
||||
.account-list-container .list-row .codicon .badge {
|
||||
position: relative;
|
||||
top: 22px;
|
||||
left: 22px;
|
||||
overflow: hidden;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
.account-list-container .list-row .codicon .badge .badge-content {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
background-size: 15px;
|
||||
}
|
||||
|
||||
/* Refresh link */
|
||||
.refresh-container {
|
||||
padding-left: 15px;
|
||||
padding-top: 6px;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.refresh-container .monaco-action-bar .actions-container {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.refresh-container .codicon {
|
||||
flex: 0 0 16px;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.refresh-container .monaco-action-bar {
|
||||
flex: 1 1 auto;
|
||||
margin-left: 10px;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.auto-oauth-dialog {
|
||||
padding: 15px
|
||||
}
|
||||
|
||||
.modal .auto-oauth-dialog .new-section {
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
.modal .auto-oauth-dialog .dialog-input-section {
|
||||
display: flex;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.modal .auto-oauth-dialog .dialog-input-section .dialog-label {
|
||||
flex: 0 0 100px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.modal .auto-oauth-dialog .dialog-input-section .dialog-input {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>new_account_16x16</title><path d="M8.09,7.39a5.91,5.91,0,0,1,1.38.74,6.06,6.06,0,0,1,1.13,1.06A6,6,0,0,1,11.88,12h-1a4.94,4.94,0,0,0-.63-1.61A5,5,0,0,0,7.66,8.3a5,5,0,0,0-3-.12,5.09,5.09,0,0,0-1.2.5,5,5,0,0,0-1.79,1.79,5.07,5.07,0,0,0-.5,1.2A4.88,4.88,0,0,0,1,13H0a5.88,5.88,0,0,1,.28-1.8A6,6,0,0,1,1,9.59a6.1,6.1,0,0,1,1.22-1.3,5.8,5.8,0,0,1,1.59-.9A4.2,4.2,0,0,1,2.46,5.94,3.86,3.86,0,0,1,2,4a3.92,3.92,0,0,1,.31-1.56A4,4,0,0,1,4.4.31a4,4,0,0,1,3.12,0A4,4,0,0,1,9.65,2.44,4,4,0,0,1,9.83,5a4,4,0,0,1-1,1.74A3.94,3.94,0,0,1,8.09,7.39ZM3,4A2.92,2.92,0,0,0,3.2,5.17a3,3,0,0,0,1.6,1.6,3,3,0,0,0,2.33,0,3,3,0,0,0,1.6-1.6,3,3,0,0,0,0-2.33,3,3,0,0,0-1.6-1.6,3,3,0,0,0-2.33,0,3,3,0,0,0-1.6,1.6A2.92,2.92,0,0,0,3,4Zm13,9v1H14v2H13V14H11V13h2V11h1v2Z"/></svg>
|
||||
|
After Width: | Height: | Size: 850 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>new_account_inverse_16x16</title><path class="cls-1" d="M8.13,7.39a5.91,5.91,0,0,1,1.38.74,6.06,6.06,0,0,1,1.13,1.06A6,6,0,0,1,11.91,12h-1a4.94,4.94,0,0,0-.63-1.61A5,5,0,0,0,7.7,8.3a5,5,0,0,0-3-.12,5.09,5.09,0,0,0-1.2.5,5,5,0,0,0-1.79,1.79,5.07,5.07,0,0,0-.5,1.2A4.88,4.88,0,0,0,1,13H0a5.88,5.88,0,0,1,.28-1.8,6,6,0,0,1,.79-1.6,6.1,6.1,0,0,1,1.22-1.3,5.8,5.8,0,0,1,1.59-.9A4.2,4.2,0,0,1,2.5,5.94,3.86,3.86,0,0,1,2,4a3.92,3.92,0,0,1,.31-1.56A4,4,0,0,1,4.44.31a4,4,0,0,1,3.12,0A4,4,0,0,1,9.69,2.44,4,4,0,0,1,9.87,5a4,4,0,0,1-1,1.74A3.94,3.94,0,0,1,8.13,7.39ZM3,4a2.92,2.92,0,0,0,.23,1.17,3,3,0,0,0,1.6,1.6,3,3,0,0,0,2.33,0,3,3,0,0,0,1.6-1.6,3,3,0,0,0,0-2.33,3,3,0,0,0-1.6-1.6,3,3,0,0,0-2.33,0,3,3,0,0,0-1.6,1.6A2.92,2.92,0,0,0,3,4Zm13,9v1H14v2H13V14H11V13h2V11h1v2Z"/></svg>
|
||||
|
After Width: | Height: | Size: 918 B |
Reference in New Issue
Block a user