diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index 29460ca8c928a..f1916ddf3f6e9 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -34,7 +34,8 @@ const BUILT_IN_AUTH_DEPENDENTS: AuthDependent[] = [ export class MainThreadAuthenticationProvider extends Disposable { private _sessionMenuItems = new Map(); - private _sessionIds: string[] = []; + private _accounts = new Map(); // Map account name to session ids + private _sessions = new Map(); // Map account id to name constructor( private readonly _proxy: ExtHostAuthenticationShape, @@ -44,10 +45,6 @@ export class MainThreadAuthenticationProvider extends Disposable { ) { super(); - if (!dependents.length) { - return; - } - this.registerCommandsAndContextMenuItems(); } @@ -86,21 +83,23 @@ export class MainThreadAuthenticationProvider extends Disposable { } private registerCommandsAndContextMenuItems(): void { - this._register(CommandsRegistry.registerCommand({ - id: `signIn${this.id}`, - handler: (accessor, args) => { - this.setPermissionsForAccount(accessor.get(IQuickInputService), true); - }, - })); - - this._register(MenuRegistry.appendMenuItem(MenuId.AccountsContext, { - group: '2_providers', - command: { + if (this.dependents.length) { + this._register(CommandsRegistry.registerCommand({ id: `signIn${this.id}`, - title: nls.localize('addAccount', "Sign in to {0}", this.displayName) - }, - order: 3 - })); + handler: (accessor, args) => { + this.setPermissionsForAccount(accessor.get(IQuickInputService), true); + }, + })); + + this._register(MenuRegistry.appendMenuItem(MenuId.AccountsContext, { + group: '2_providers', + command: { + id: `signIn${this.id}`, + title: nls.localize('addAccount', "Sign in to {0}", this.displayName) + }, + order: 3 + })); + } this._proxy.$getSessions(this.id).then(sessions => { sessions.forEach(session => this.registerSession(session)); @@ -108,7 +107,16 @@ export class MainThreadAuthenticationProvider extends Disposable { } private registerSession(session: modes.AuthenticationSession) { - this._sessionIds.push(session.id); + this._sessions.set(session.id, session.accountName); + + const existingSessionsForAccount = this._accounts.get(session.accountName); + if (existingSessionsForAccount) { + this._accounts.set(session.accountName, existingSessionsForAccount.concat(session.id)); + return; + } else { + this._accounts.set(session.accountName, [session.id]); + } + const menuItem = MenuRegistry.appendMenuItem(MenuId.AccountsContext, { group: '1_accounts', command: { @@ -131,7 +139,8 @@ export class MainThreadAuthenticationProvider extends Disposable { quickPick.onDidAccept(e => { const selected = quickPick.selectedItems[0]; if (selected.label === 'Sign Out') { - this.logout(session.id); + const sessionsForAccount = this._accounts.get(session.accountName); + sessionsForAccount?.forEach(sessionId => this.logout(sessionId)); } quickPick.dispose(); @@ -145,7 +154,7 @@ export class MainThreadAuthenticationProvider extends Disposable { }, }); - this._sessionMenuItems.set(session.id, [menuItem, manageCommand]); + this._sessionMenuItems.set(session.accountName, [menuItem, manageCommand]); } async getSessions(): Promise> { @@ -158,22 +167,29 @@ export class MainThreadAuthenticationProvider extends Disposable { }); } - async updateSessionItems(): Promise { - const currentSessions = await this._proxy.$getSessions(this.id); - const removedSessionIds = this._sessionIds.filter(id => !currentSessions.some(session => session.id === id)); - const addedSessions = currentSessions.filter(session => !this._sessionIds.some(id => id === session.id)); - - removedSessionIds.forEach(id => { - const disposeables = this._sessionMenuItems.get(id); - if (disposeables) { - disposeables.forEach(disposeable => disposeable.dispose()); - this._sessionMenuItems.delete(id); + async updateSessionItems(event: modes.AuthenticationSessionsChangeEvent): Promise { + const { added, removed } = event; + const session = await this._proxy.$getSessions(this.id); + const addedSessions = session.filter(session => added.some(id => id === session.id)); + + removed.forEach(sessionId => { + const accountName = this._sessions.get(sessionId); + if (accountName) { + let sessionsForAccount = this._accounts.get(accountName) || []; + const sessionIndex = sessionsForAccount.indexOf(sessionId); + sessionsForAccount.splice(sessionIndex); + + if (!sessionsForAccount.length) { + const disposeables = this._sessionMenuItems.get(accountName); + if (disposeables) { + disposeables.forEach(disposeable => disposeable.dispose()); + this._sessionMenuItems.delete(accountName); + } + } } }); addedSessions.forEach(session => this.registerSession(session)); - - this._sessionIds = currentSessions.map(session => session.id); } login(scopes: string[]): Promise { diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 065099a486fdd..0d14030700496 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -53,6 +53,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { timeout } from 'vs/base/common/async'; +import { distinct } from 'vs/base/common/arrays'; const enum AuthStatus { Initializing = 'Initializing', @@ -251,7 +252,8 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo const quickPick = this.quickInputService.createQuickPick<{ label: string, session: AuthenticationSession }>(); quickPick.title = localize('chooseAccountTitle', "Preferences Sync: Choose Account"); quickPick.placeholder = localize('chooseAccount', "Choose an account you would like to use for settings sync"); - quickPick.items = sessions.map(session => { + const dedupedSessions = distinct(sessions, (session) => session.accountName); + quickPick.items = dedupedSessions.map(session => { return { label: session.accountName, session: session diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts index 4bb4455575321..69ae38aff8fc4 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts @@ -88,7 +88,7 @@ export class AuthenticationService extends Disposable implements IAuthentication this._onDidChangeSessions.fire({ providerId: id, event: event }); const provider = this._authenticationProviders.get(id); if (provider) { - provider.updateSessionItems(); + provider.updateSessionItems(event); } }