提交 7decc00e 编写于 作者: S Sandeep Somavarapu

Refactor UserDataSyncAccounts

- Introduce account status
- Update accounts and current session id
- Update other logic based on above state
上级 190e13f0
......@@ -51,7 +51,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 { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService';
import { UserDataSyncAccountManager } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncAccount';
import { UserDataSyncAccounts, AccountStatus } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncAccount';
const CONTEXT_CONFLICTS_SOURCES = new RawContextKey<string>('conflictsSources', '');
......@@ -85,13 +85,13 @@ const getActivityTitle = (label: string, userDataSyncService: IUserDataSyncServi
}
return label;
};
const getIdentityTitle = (label: string, userDataSyncAccountService: UserDataSyncAccountManager, authenticationService: IAuthenticationService) => {
const activeAccount = userDataSyncAccountService.activeAccount;
return activeAccount ? `${label} (${authenticationService.getDisplayName(activeAccount.providerId)}:${activeAccount.accountName})` : label;
const getIdentityTitle = (label: string, userDataSyncAccountService: UserDataSyncAccounts, authenticationService: IAuthenticationService) => {
const account = userDataSyncAccountService.current;
return account ? `${label} (${authenticationService.getDisplayName(account.providerId)}:${account.accountName})` : label;
};
const turnOnSyncCommand = { id: 'workbench.userData.actions.syncStart', title: localize('turn on sync with category', "Preferences Sync: Turn on...") };
const signInCommand = { id: 'workbench.userData.actions.signin', title: localize('sign in', "Preferences Sync: Sign in to sync") };
const stopSyncCommand = { id: 'workbench.userData.actions.stopSync', title(userDataSyncAccountService: UserDataSyncAccountManager, authenticationService: IAuthenticationService) { return getIdentityTitle(localize('stop sync', "Preferences Sync: Turn Off"), userDataSyncAccountService, authenticationService); } };
const stopSyncCommand = { id: 'workbench.userData.actions.stopSync', title(userDataSyncAccountService: UserDataSyncAccounts, authenticationService: IAuthenticationService) { return getIdentityTitle(localize('stop sync', "Preferences Sync: Turn Off"), userDataSyncAccountService, authenticationService); } };
const resolveSettingsConflictsCommand = { id: 'workbench.userData.actions.resolveSettingsConflicts', title: localize('showConflicts', "Preferences Sync: Show Settings Conflicts") };
const resolveKeybindingsConflictsCommand = { id: 'workbench.userData.actions.resolveKeybindingsConflicts', title: localize('showKeybindingsConflicts', "Preferences Sync: Show Keybindings Conflicts") };
const resolveSnippetsConflictsCommand = { id: 'workbench.userData.actions.resolveSnippetsConflicts', title: localize('showSnippetsConflicts', "Preferences Sync: Show User Snippets Conflicts") };
......@@ -103,21 +103,16 @@ const showSyncActivityCommand = {
};
const showSyncSettingsCommand = { id: 'workbench.userData.actions.syncSettings', title: localize('sync settings', "Preferences Sync: Show Settings"), };
const CONTEXT_ACTIVE_ACCOUNT_STATE = new RawContextKey<string>('activeAccountStatus', SyncStatus.Uninitialized);
const enum ActiveAccountStatus {
Uninitialized = 'uninitialized',
Active = 'active',
Inactive = 'inactive'
}
const CONTEXT_ACCOUNT_STATE = new RawContextKey<string>('userDataSyncAccountStatus', AccountStatus.Uninitialized);
export class UserDataSyncWorkbenchContribution extends Disposable implements IWorkbenchContribution {
private readonly syncEnablementContext: IContextKey<boolean>;
private readonly syncStatusContext: IContextKey<string>;
private readonly activeAccountStatusContext: IContextKey<string>;
private readonly accountStatusContext: IContextKey<string>;
private readonly conflictsSources: IContextKey<string>;
private readonly userDataSyncAccountManager: UserDataSyncAccountManager;
private readonly userDataSyncAccounts: UserDataSyncAccounts;
private readonly badgeDisposable = this._register(new MutableDisposable());
constructor(
......@@ -146,25 +141,25 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
this.syncEnablementContext = CONTEXT_SYNC_ENABLEMENT.bindTo(contextKeyService);
this.syncStatusContext = CONTEXT_SYNC_STATE.bindTo(contextKeyService);
this.activeAccountStatusContext = CONTEXT_ACTIVE_ACCOUNT_STATE.bindTo(contextKeyService);
this.accountStatusContext = CONTEXT_ACCOUNT_STATE.bindTo(contextKeyService);
this.conflictsSources = CONTEXT_CONFLICTS_SOURCES.bindTo(contextKeyService);
this.userDataSyncAccountManager = instantiationService.createInstance(UserDataSyncAccountManager);
this.userDataSyncAccounts = instantiationService.createInstance(UserDataSyncAccounts);
if (this.userDataSyncAccountManager.userDataSyncAccountProvider) {
if (this.userDataSyncAccounts.accountProviderId) {
registerConfiguration();
this.onDidChangeSyncStatus(this.userDataSyncService.status);
this.onDidChangeConflicts(this.userDataSyncService.conflicts);
this.onDidChangeEnablement(this.userDataSyncEnablementService.isEnabled());
this.onDidChangeActiveAccount();
this.onDidChangeAccountStatus(this.userDataSyncAccounts.status);
this._register(Event.debounce(userDataSyncService.onDidChangeStatus, () => undefined, 500)(() => this.onDidChangeSyncStatus(this.userDataSyncService.status)));
this._register(userDataSyncService.onDidChangeConflicts(() => this.onDidChangeConflicts(this.userDataSyncService.conflicts)));
this._register(userDataSyncService.onSyncErrors(errors => this.onSyncErrors(errors)));
this._register(this.userDataSyncEnablementService.onDidChangeEnablement(enabled => this.onDidChangeEnablement(enabled)));
this._register(userDataAutoSyncService.onError(error => this.onAutoSyncError(error)));
this._register(this.userDataSyncAccountManager.onDidChangeActiveAccount(() => this.onDidChangeActiveAccount()));
this._register(this.userDataSyncAccounts.onDidChangeStatus(status => this.onDidChangeAccountStatus(status)));
this.registerActions();
textModelResolverService.registerTextModelContentProvider(USER_DATA_SYNC_SCHEME, instantiationService.createInstance(UserDataRemoteContentProvider));
......@@ -176,11 +171,12 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
}
}
private onDidChangeActiveAccount(): void {
const activeAccount = this.userDataSyncAccountManager.activeAccount;
this.activeAccountStatusContext.set(activeAccount === undefined ? ActiveAccountStatus.Uninitialized
: activeAccount === null ? ActiveAccountStatus.Inactive : ActiveAccountStatus.Active);
private onDidChangeAccountStatus(status: AccountStatus): void {
this.accountStatusContext.set(status);
this.updateBadge();
if (status === AccountStatus.Unavailable) {
this.doTurnOff(false);
}
}
private onDidChangeSyncStatus(status: SyncStatus) {
......@@ -394,7 +390,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
let clazz: string | undefined;
let priority: number | undefined = undefined;
if (this.userDataSyncService.status !== SyncStatus.Uninitialized && this.userDataSyncEnablementService.isEnabled() && this.userDataSyncAccountManager.activeAccount === null) {
if (this.userDataSyncService.status !== SyncStatus.Uninitialized && this.userDataSyncEnablementService.isEnabled() && this.userDataSyncAccounts.status === AccountStatus.Inactive) {
badge = new NumberBadge(1, () => localize('sign in to sync', "Sign in to Sync"));
} else if (this.userDataSyncService.conflicts.length) {
badge = new NumberBadge(this.userDataSyncService.conflicts.reduce((result, syncResourceConflict) => { return result + syncResourceConflict.conflicts.length; }, 0), () => localize('has conflicts', "Preferences Sync: Conflicts Detected"));
......@@ -432,12 +428,13 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
quickPick.title = localize('turn on title', "Preferences Sync: Turn On");
quickPick.ok = false;
quickPick.customButton = true;
if (this.userDataSyncAccountManager.activeAccount) {
quickPick.customLabel = localize('turn on', "Turn On");
} else {
const displayName = this.authenticationService.getDisplayName(this.userDataSyncAccountManager.userDataSyncAccountProvider!);
const requiresLogin = this.userDataSyncAccounts.all.length === 0;
if (requiresLogin) {
const displayName = this.authenticationService.getDisplayName(this.userDataSyncAccounts.accountProviderId!);
quickPick.description = localize('sign in and turn on sync detail', "Sign in with your {0} account to synchronize your data across devices.", displayName);
quickPick.customLabel = localize('sign in and turn on sync', "Sign in & Turn on");
} else {
quickPick.customLabel = localize('turn on', "Turn On");
}
quickPick.placeholder = localize('configure sync placeholder', "Choose what to sync");
quickPick.canSelectMany = true;
......@@ -448,7 +445,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
disposables.add(Event.any(quickPick.onDidAccept, quickPick.onDidCustom)(async () => {
if (quickPick.selectedItems.length) {
this.updateConfiguration(items, quickPick.selectedItems);
this.doTurnOn().then(c, e);
this.doTurnOn(requiresLogin).then(c, e);
quickPick.hide();
}
}));
......@@ -457,17 +454,16 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
});
}
private async doTurnOn(): Promise<void> {
// If this was not triggered by signing in from the accounts menu, show accounts list
if (this.userDataSyncAccountManager.activeAccount) {
await this.userDataSyncAccountManager.select();
private async doTurnOn(requiresLogin: boolean): Promise<void> {
if (requiresLogin) {
await this.userDataSyncAccounts.login();
} else {
await this.userDataSyncAccountManager.login();
await this.userDataSyncAccounts.select();
}
// User did not pick an account or login failed, no need to continue
if (!this.userDataSyncAccountManager.activeAccount) {
return;
// User did not pick an account or login failed
if (this.userDataSyncAccounts.status !== AccountStatus.Active) {
throw new Error(localize('no account', "No account available"));
}
await this.handleFirstTimeSync();
......@@ -575,14 +571,18 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
}
});
if (result.confirmed) {
if (result.checkboxChecked) {
this.telemetryService.publicLog2('sync/turnOffEveryWhere');
await this.userDataSyncService.reset();
} else {
await this.userDataSyncService.resetLocal();
}
this.disableSync();
return this.doTurnOff(!!result.checkboxChecked);
}
}
private async doTurnOff(turnOffEveryWhere: boolean): Promise<void> {
if (turnOffEveryWhere) {
this.telemetryService.publicLog2('sync/turnOffEveryWhere');
await this.userDataSyncService.reset();
} else {
await this.userDataSyncService.resetLocal();
}
this.disableSync();
}
private disableSync(source?: SyncResource): void {
......@@ -664,7 +664,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
}
private registerTurnOnSyncAction(): void {
const turnOnSyncWhenContext = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT.toNegated(), CONTEXT_ACTIVE_ACCOUNT_STATE.notEqualsTo(ActiveAccountStatus.Uninitialized));
const turnOnSyncWhenContext = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT.toNegated(), CONTEXT_ACCOUNT_STATE.notEqualsTo(AccountStatus.Uninitialized));
CommandsRegistry.registerCommand(turnOnSyncCommand.id, async () => {
try {
await this.turnOn();
......@@ -715,14 +715,14 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
menu: {
group: '5_sync',
id: MenuId.GlobalActivity,
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT, CONTEXT_ACTIVE_ACCOUNT_STATE.isEqualTo(ActiveAccountStatus.Inactive)),
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT, CONTEXT_ACCOUNT_STATE.isEqualTo(AccountStatus.Inactive)),
order: 2
},
});
}
async run(): Promise<any> {
try {
await that.userDataSyncAccountManager.login();
await that.userDataSyncAccounts.login();
} catch (e) {
that.notificationService.error(e);
}
......@@ -816,7 +816,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
private registerSyncStatusAction(): void {
const that = this;
const when = ContextKeyExpr.and(CONTEXT_SYNC_ENABLEMENT, CONTEXT_ACTIVE_ACCOUNT_STATE.isEqualTo(ActiveAccountStatus.Active), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized));
const when = ContextKeyExpr.and(CONTEXT_SYNC_ENABLEMENT, CONTEXT_ACCOUNT_STATE.isEqualTo(AccountStatus.Active), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized));
this._register(registerAction2(class SyncStatusAction extends Action2 {
constructor() {
super({
......@@ -871,7 +871,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
items.push({ id: showSyncSettingsCommand.id, label: showSyncSettingsCommand.title });
items.push({ id: showSyncActivityCommand.id, label: showSyncActivityCommand.title(that.userDataSyncService) });
items.push({ type: 'separator' });
items.push({ id: stopSyncCommand.id, label: stopSyncCommand.title(that.userDataSyncAccountManager, that.authenticationService) });
items.push({ id: stopSyncCommand.id, label: stopSyncCommand.title(that.userDataSyncAccounts, that.authenticationService) });
quickPick.items = items;
disposables.add(quickPick.onDidAccept(() => {
if (quickPick.selectedItems[0] && quickPick.selectedItems[0].id) {
......@@ -895,7 +895,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
constructor() {
super({
id: stopSyncCommand.id,
title: stopSyncCommand.title(that.userDataSyncAccountManager, that.authenticationService),
title: stopSyncCommand.title(that.userDataSyncAccounts, that.authenticationService),
menu: {
id: MenuId.CommandPalette,
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT),
......
......@@ -7,15 +7,16 @@ import { IAuthenticationService } from 'vs/workbench/services/authentication/bro
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
import { IProductService } from 'vs/platform/product/common/productService';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage';
import { localize } from 'vs/nls';
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { AuthenticationSession } from 'vs/editor/common/modes';
import { AuthenticationSession, AuthenticationSessionsChangeEvent } from 'vs/editor/common/modes';
import { Event, Emitter } from 'vs/base/common/event';
import { getUserDataSyncStore, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { distinct } from 'vs/base/common/arrays';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { values } from 'vs/base/common/map';
import { ILogService } from 'vs/platform/log/common/log';
type UserAccountClassification = {
id: { classification: 'EndUserPseudonymizedInformation', purpose: 'BusinessInsight' };
......@@ -26,23 +27,35 @@ type UserAccountEvent = {
};
export interface IUserDataSyncAccount {
providerId: string;
sessionId: string;
accountName: string;
readonly providerId: string;
readonly sessionId: string;
readonly accountName: string;
}
export class UserDataSyncAccountManager extends Disposable {
export const enum AccountStatus {
Uninitialized = 'uninitialized',
Unavailable = 'unavailable',
Inactive = 'inactive',
Active = 'active',
}
export class UserDataSyncAccounts extends Disposable {
private static LAST_USED_SESSION_STORAGE_KEY = 'userDataSyncAccountPreference';
private static CACHED_SESSION_STORAGE_KEY = 'userDataSyncAccountPreference';
_serviceBrand: any;
readonly userDataSyncAccountProvider: string | undefined;
readonly accountProviderId: string | undefined;
private _status: AccountStatus = AccountStatus.Uninitialized;
get status(): AccountStatus { return this._status; }
private readonly _onDidChangeStatus = this._register(new Emitter<AccountStatus>());
readonly onDidChangeStatus = this._onDidChangeStatus.event;
private _activeAccount: IUserDataSyncAccount | undefined | null;
get activeAccount(): IUserDataSyncAccount | undefined | null { return this._activeAccount; }
private readonly _onDidChangeActiveAccount = this._register(new Emitter<{ previous: IUserDataSyncAccount | undefined | null, current: IUserDataSyncAccount | null }>());
readonly onDidChangeActiveAccount = this._onDidChangeActiveAccount.event;
private _all: IUserDataSyncAccount[] = [];
get all(): IUserDataSyncAccount[] { return this._all; }
get current(): IUserDataSyncAccount | undefined { return this._all.filter(({ sessionId }) => sessionId === this.currentSessionId)[0]; }
constructor(
@IAuthenticationService private readonly authenticationService: IAuthenticationService,
......@@ -51,98 +64,130 @@ export class UserDataSyncAccountManager extends Disposable {
@IStorageService private readonly storageService: IStorageService,
@IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@ILogService private readonly logService: ILogService,
@IProductService productService: IProductService,
@IConfigurationService configurationService: IConfigurationService,
) {
super();
this.userDataSyncAccountProvider = getUserDataSyncStore(productService, configurationService)?.authenticationProviderId;
if (this.userDataSyncAccountProvider) {
if (authenticationService.isAuthenticationProviderRegistered(this.userDataSyncAccountProvider)) {
this.accountProviderId = getUserDataSyncStore(productService, configurationService)?.authenticationProviderId;
if (this.accountProviderId) {
if (authenticationService.isAuthenticationProviderRegistered(this.accountProviderId)) {
this.initialize();
} else {
this._register(Event.once(Event.filter(this.authenticationService.onDidRegisterAuthenticationProvider, providerId => providerId === this.userDataSyncAccountProvider))(() => this.initialize()));
this._register(Event.once(Event.filter(this.authenticationService.onDidRegisterAuthenticationProvider, providerId => providerId === this.accountProviderId))(() => this.initialize()));
}
}
}
private async initialize(): Promise<void> {
await this.update();
this._register(
Event.any(
Event.filter(
Event.any(
this.authenticationService.onDidRegisterAuthenticationProvider,
this.authenticationService.onDidUnregisterAuthenticationProvider,
Event.map(this.authenticationService.onDidChangeSessions, e => e.providerId)
), providerId => providerId === this.userDataSyncAccountProvider),
), providerId => providerId === this.accountProviderId),
this.authenticationTokenService.onTokenFailed)
(() => this.update()));
this._register(Event.filter(this.authenticationService.onDidChangeSessions, e => e.providerId === this.accountProviderId)(({ event }) => this.onDidChangeSessions(event)));
this._register(this.storageService.onDidChangeStorage(e => this.onDidChangeStorage(e)));
}
private async update(): Promise<void> {
if (!this.userDataSyncAccountProvider) {
return;
}
let activeSession: AuthenticationSession | undefined = undefined;
if (this.lastUsedSessionId) {
const sessions = await this.authenticationService.getSessions(this.userDataSyncAccountProvider);
if (sessions?.length) {
activeSession = sessions.find(session => session.id === this.lastUsedSessionId);
let status = AccountStatus.Unavailable;
let allAccounts: Map<string, IUserDataSyncAccount> = new Map<string, IUserDataSyncAccount>();
if (this.accountProviderId) {
let currentAccount: IUserDataSyncAccount | null = null;
let currentSession: AuthenticationSession | undefined = undefined;
if (this.currentSessionId) {
const sessions = await this.authenticationService.getSessions(this.accountProviderId) || [];
for (const session of sessions) {
const account: IUserDataSyncAccount = { providerId: this.accountProviderId, sessionId: session.id, accountName: session.accountName };
allAccounts.set(account.accountName, account);
if (session.id === this.currentSessionId) {
currentSession = session;
currentAccount = account;
}
}
}
if (currentSession) {
status = AccountStatus.Inactive;
try {
const token = await currentSession.getAccessToken();
await this.authenticationTokenService.setToken(token);
status = AccountStatus.Active;
} catch (e) {
// Ignore error
}
}
}
let activeAccount: IUserDataSyncAccount | null = null;
if (activeSession) {
try {
const token = await activeSession.getAccessToken();
await this.authenticationTokenService.setToken(token);
activeAccount = {
providerId: this.userDataSyncAccountProvider,
sessionId: activeSession.id,
accountName: activeSession.accountName
};
} catch (e) {
// Ignore and log error
if (currentAccount) {
// Always use current account if available
allAccounts.set(currentAccount.accountName, currentAccount);
}
}
if (!this.areSameAccounts(activeAccount, this._activeAccount)) {
const previous = this._activeAccount;
this._activeAccount = activeAccount;
this._onDidChangeActiveAccount.fire({ previous, current: this._activeAccount });
this._all = values(allAccounts);
if (this._status !== status) {
this._status = status;
this.logService.debug('Sync account status changed', this._status, status);
this._onDidChangeStatus.fire(status);
}
}
async login(): Promise<void> {
if (this.userDataSyncAccountProvider) {
const session = await this.authenticationService.login(this.userDataSyncAccountProvider, ['https://management.core.windows.net/.default', 'offline_access']);
await this.switch(session.id);
if (this.accountProviderId) {
const session = await this.authenticationService.login(this.accountProviderId, ['https://management.core.windows.net/.default', 'offline_access']);
await this.switch(session.id, session.accountName);
}
}
async select(): Promise<void> {
if (!this.activeAccount) {
if (!this.accountProviderId) {
return;
}
if (!this.all.length) {
throw new Error('Requires Login');
}
await this.update();
if (!this.activeAccount) {
if (!this.all.length) {
throw new Error('Requires Login');
}
const { providerId, sessionId } = this.activeAccount;
await new Promise(async (c, e) => {
const disposables: DisposableStore = new DisposableStore();
const quickPick = this.quickInputService.createQuickPick<{ label: string, session?: AuthenticationSession, detail?: string }>();
const quickPick = this.quickInputService.createQuickPick<{ label: string, account?: IUserDataSyncAccount, detail?: string }>();
disposables.add(quickPick);
quickPick.title = localize('pick account', "{0}: Pick an account", this.authenticationService.getDisplayName(providerId));
quickPick.title = localize('pick account', "{0}: Pick an account", this.authenticationService.getDisplayName(this.accountProviderId!));
quickPick.ok = false;
quickPick.placeholder = localize('choose account placeholder', "Pick an account for syncing");
quickPick.ignoreFocusOut = true;
const currentAccount = this.current;
const accounts = currentAccount
? [currentAccount, ...this._all.filter(account => account.sessionId !== this.current!.sessionId)]
: this._all;
quickPick.items = [...accounts.map(account => ({
label: account.accountName,
account: account,
detail: account.sessionId === this.current?.sessionId ? localize('last used', "Last Used") : undefined
})), { label: localize('choose another', "Use another account") }];
disposables.add(quickPick.onDidAccept(async () => {
const selected = quickPick.selectedItems[0];
if (selected) {
if (selected.session) {
await this.switch(selected.session.id);
if (selected.account) {
await this.switch(selected.account.sessionId, selected.account.accountName);
} else {
await this.login();
}
......@@ -152,66 +197,55 @@ export class UserDataSyncAccountManager extends Disposable {
}));
disposables.add(quickPick.onDidHide(() => disposables.dispose()));
quickPick.show();
quickPick.busy = true;
quickPick.items = await this.getSessionQuickPickItems(providerId, sessionId);
quickPick.busy = false;
});
}
async switch(sessionId: string): Promise<void> {
if (this.userDataSyncEnablementService.isEnabled() && (this.lastUsedSessionId && this.lastUsedSessionId !== sessionId)) {
private async switch(sessionId: string, accountName: string): Promise<void> {
const currentAccount = this.current;
if (this.userDataSyncEnablementService.isEnabled() && (currentAccount && currentAccount.accountName !== accountName)) {
// accounts are switched while sync is enabled.
}
this.lastUsedSessionId = sessionId;
this.currentSessionId = sessionId;
this.telemetryService.publicLog2<UserAccountEvent, UserAccountClassification>('sync.userAccount', { id: sessionId.split('/')[1] });
await this.update();
}
private async getSessionQuickPickItems(providerId: string, sessionId: string): Promise<{ label: string, session?: AuthenticationSession, detail?: string }[]> {
const quickPickItems: { label: string, session?: AuthenticationSession, detail?: string }[] = [];
let sessions = await this.authenticationService.getSessions(providerId) || [];
const lastUsedSession = sessions.filter(session => session.id === sessionId)[0];
if (lastUsedSession) {
sessions = sessions.filter(session => session.accountName !== lastUsedSession.accountName);
quickPickItems.push({
label: lastUsedSession.accountName,
session: lastUsedSession,
detail: localize('previously used', "Last used")
});
private onDidChangeSessions(e: AuthenticationSessionsChangeEvent): void {
if (this.currentSessionId && e.removed.includes(this.currentSessionId)) {
this.currentSessionId = undefined;
}
quickPickItems.push(...distinct(sessions, session => session.accountName).map(session => ({ label: session.accountName, session })));
quickPickItems.push({ label: localize('choose another', "Use another account") });
return quickPickItems;
this.update();
}
private get lastUsedSessionId(): string | undefined {
return this.storageService.get(UserDataSyncAccountManager.LAST_USED_SESSION_STORAGE_KEY, StorageScope.GLOBAL);
private onDidChangeStorage(e: IWorkspaceStorageChangeEvent): void {
if (e.key === UserDataSyncAccounts.CACHED_SESSION_STORAGE_KEY && e.scope === StorageScope.GLOBAL
&& this.currentSessionId !== this.getStoredCachedSessionId() /* This checks if current window changed the value or not */) {
this._cachedCurrentSessionId = null;
this.update();
}
}
private set lastUsedSessionId(lastUserSessionId: string | undefined) {
if (lastUserSessionId === undefined) {
this.storageService.remove(UserDataSyncAccountManager.LAST_USED_SESSION_STORAGE_KEY, StorageScope.GLOBAL);
} else {
this.storageService.store(UserDataSyncAccountManager.LAST_USED_SESSION_STORAGE_KEY, lastUserSessionId, StorageScope.GLOBAL);
private _cachedCurrentSessionId: string | undefined | null = null;
private get currentSessionId(): string | undefined {
if (this._cachedCurrentSessionId === null) {
this._cachedCurrentSessionId = this.getStoredCachedSessionId();
}
return this._cachedCurrentSessionId;
}
private areSameAccounts(a: IUserDataSyncAccount | undefined | null, b: IUserDataSyncAccount | undefined | null): boolean {
if (a === b) {
return true;
}
if (a && b
&& a.providerId === b.providerId
&& a.sessionId === b.sessionId
) {
return true;
private set currentSessionId(cachedSessionId: string | undefined) {
if (this.currentSessionId !== cachedSessionId) {
this._cachedCurrentSessionId = cachedSessionId;
if (cachedSessionId === undefined) {
this.storageService.remove(UserDataSyncAccounts.CACHED_SESSION_STORAGE_KEY, StorageScope.GLOBAL);
} else {
this.storageService.store(UserDataSyncAccounts.CACHED_SESSION_STORAGE_KEY, cachedSessionId, StorageScope.GLOBAL);
}
}
return false;
}
private getStoredCachedSessionId(): string | undefined {
return this.storageService.get(UserDataSyncAccounts.CACHED_SESSION_STORAGE_KEY, StorageScope.GLOBAL);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册