Improve edit sessions authentication usability (#153011)

* Add account menu item to sign out of edit sessions

* Reset auth on token failure

* Improve auth quickpick title
This commit is contained in:
Joyce Er 2022-06-23 12:57:29 -07:00 committed by GitHub
parent 25cc6c0dd0
commit 0a432d2bba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 32 additions and 9 deletions

View file

@ -7,6 +7,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions';
import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IFileService } from 'vs/platform/files/common/files';
import { ILogService } from 'vs/platform/log/common/log';
@ -18,7 +19,7 @@ import { IAuthenticationProvider } from 'vs/platform/userDataSync/common/userDat
import { UserDataSyncStoreClient } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
import { AuthenticationSession, AuthenticationSessionsChangeEvent, IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { EditSession, EDIT_SESSION_SYNC_TITLE, ISessionSyncWorkbenchService } from 'vs/workbench/services/sessionSync/common/sessionSync';
import { EDIT_SESSIONS_SIGNED_IN, EditSession, EDIT_SESSION_SYNC_TITLE, ISessionSyncWorkbenchService, EDIT_SESSIONS_SIGNED_IN_KEY } from 'vs/workbench/services/sessionSync/common/sessionSync';
type ExistingSession = IQuickPickItem & { session: AuthenticationSession & { providerId: string } };
type AuthenticationProviderOption = IQuickPickItem & { provider: IAuthenticationProvider };
@ -34,6 +35,7 @@ export class SessionSyncWorkbenchService extends Disposable implements ISessionS
private static CACHED_SESSION_STORAGE_KEY = 'editSessionSyncAccountPreference';
private initialized = false;
private readonly signedInContext: IContextKey<boolean>;
constructor(
@IFileService private readonly fileService: IFileService,
@ -44,6 +46,7 @@ export class SessionSyncWorkbenchService extends Disposable implements ISessionS
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@ILogService private readonly logService: ILogService,
@IProductService private readonly productService: IProductService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IRequestService private readonly requestService: IRequestService,
) {
super();
@ -55,6 +58,9 @@ export class SessionSyncWorkbenchService extends Disposable implements ISessionS
this._register(this.storageService.onDidChangeValue(e => this.onDidChangeStorage(e)));
this.registerResetAuthenticationAction();
this.signedInContext = EDIT_SESSIONS_SIGNED_IN.bindTo(this.contextKeyService);
this.signedInContext.set(this.existingSessionId !== undefined);
}
/**
@ -63,7 +69,7 @@ export class SessionSyncWorkbenchService extends Disposable implements ISessionS
* @returns The ref of the stored edit session state.
*/
async write(editSession: EditSession): Promise<string> {
this.initialized = await this.waitAndInitialize();
await this.initialize();
if (!this.initialized) {
throw new Error('Please sign in to store your edit session.');
}
@ -78,7 +84,7 @@ export class SessionSyncWorkbenchService extends Disposable implements ISessionS
* @returns An object representing the requested or latest edit session state, if any.
*/
async read(ref: string | undefined): Promise<{ ref: string; editSession: EditSession } | undefined> {
this.initialized = await this.waitAndInitialize();
await this.initialize();
if (!this.initialized) {
throw new Error('Please sign in to apply your latest edit session.');
}
@ -101,7 +107,7 @@ export class SessionSyncWorkbenchService extends Disposable implements ISessionS
}
async delete(ref: string) {
this.initialized = await this.waitAndInitialize();
await this.initialize();
if (!this.initialized) {
throw new Error(`Unable to delete edit session with ref ${ref}.`);
}
@ -113,13 +119,18 @@ export class SessionSyncWorkbenchService extends Disposable implements ISessionS
}
}
private async initialize() {
this.initialized = await this.doInitialize();
this.signedInContext.set(this.initialized);
}
/**
*
* Ensures that the store client is initialized,
* meaning that authentication is configured and it
* can be used to communicate with the remote storage service
*/
private async waitAndInitialize(): Promise<boolean> {
private async doInitialize(): Promise<boolean> {
// Wait for authentication extensions to be registered
await this.extensionService.whenInstalledExtensionsRegistered();
@ -129,6 +140,7 @@ export class SessionSyncWorkbenchService extends Disposable implements ISessionS
if (!this.storeClient) {
this.storeClient = new UserDataSyncStoreClient(URI.parse(this.serverConfiguration.url), this.productService, this.requestService, this.logService, this.environmentService, this.fileService, this.storageService);
this._register(this.storeClient.onTokenFailed(() => this.clearAuthenticationPreference()));
}
// If we already have an existing auth session in memory, use that
@ -164,7 +176,7 @@ export class SessionSyncWorkbenchService extends Disposable implements ISessionS
*/
private async getAccountPreference(): Promise<AuthenticationSession & { providerId: string } | undefined> {
const quickpick = this.quickInputService.createQuickPick<ExistingSession | AuthenticationProviderOption>();
quickpick.title = localize('account preference', 'Edit Sessions');
quickpick.title = localize('account preference', 'Sign In to Use Edit Sessions');
quickpick.ok = false;
quickpick.placeholder = localize('choose account placeholder', "Select an account to sign in");
quickpick.ignoreFocusOut = true;
@ -295,6 +307,7 @@ export class SessionSyncWorkbenchService extends Disposable implements ISessionS
this.#authenticationInfo = undefined;
this.initialized = false;
this.existingSessionId = undefined;
this.signedInContext.set(false);
}
private onDidChangeSessions(e: AuthenticationSessionsChangeEvent): void {
@ -309,10 +322,16 @@ export class SessionSyncWorkbenchService extends Disposable implements ISessionS
constructor() {
super({
id: 'workbench.sessionSync.actions.resetAuth',
title: localize('reset auth', '{0}: Reset Authentication State', EDIT_SESSION_SYNC_TITLE),
menu: {
title: localize('reset auth', '{0}: Sign Out', EDIT_SESSION_SYNC_TITLE),
precondition: ContextKeyExpr.equals(EDIT_SESSIONS_SIGNED_IN_KEY, true),
menu: [{
id: MenuId.CommandPalette,
}
},
{
id: MenuId.AccountsContext,
group: '2_editSessions',
when: ContextKeyExpr.equals(EDIT_SESSIONS_SIGNED_IN_KEY, true),
}]
});
}

View file

@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const EDIT_SESSION_SYNC_TITLE = localize('session sync', 'Edit Sessions');
@ -53,3 +54,6 @@ export interface EditSession {
version: number;
folders: Folder[];
}
export const EDIT_SESSIONS_SIGNED_IN_KEY = 'editSessionsSignedIn';
export const EDIT_SESSIONS_SIGNED_IN = new RawContextKey<boolean>(EDIT_SESSIONS_SIGNED_IN_KEY, false);