From 3d82ef764c0798b570f13160f44f58725e643da2 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Wed, 7 Sep 2022 11:41:13 -0700 Subject: [PATCH 1/6] Show account menu item to turn on edit sessions --- .../editSessions/browser/editSessionsStorageService.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts index 6f4d49ddd20d5..080c82425bb4d 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts @@ -384,11 +384,16 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes constructor() { super({ id: 'workbench.editSessions.actions.signIn', - title: localize('sign in', 'Sign In'), + title: localize('sign in', 'Turn on Edit Sessions...'), category: EDIT_SESSION_SYNC_CATEGORY, precondition: ContextKeyExpr.equals(EDIT_SESSIONS_SIGNED_IN_KEY, false), menu: [{ id: MenuId.CommandPalette, + }, + { + id: MenuId.AccountsContext, + group: '2_editSessions', + when: ContextKeyExpr.equals(EDIT_SESSIONS_SIGNED_IN_KEY, false), }] }); } From 96e15897720749505c504d219cb3a8b2a59feb24 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Wed, 7 Sep 2022 14:29:25 -0700 Subject: [PATCH 2/6] Rename Continue Edit Session to Continue On --- .../contrib/editSessions/browser/editSessions.contribution.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts index a399f2b836011..4733f13b6c4c0 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts @@ -59,8 +59,7 @@ registerSingleton(IEditSessionsStorageService, EditSessionsWorkbenchService, fal const continueEditSessionCommand: IAction2Options = { id: '_workbench.experimental.editSessions.actions.continueEditSession', - title: { value: localize('continue edit session', "Continue Edit Session..."), original: 'Continue Edit Session...' }, - category: EDIT_SESSION_SYNC_CATEGORY, + title: { value: localize('continue working on', "Continue Working On..."), original: 'Continue Working On...' }, precondition: WorkspaceFolderCountContext.notEqualsTo('0'), f1: true }; From 1fb21cd69bfe8aba326b06d61fe502bd1e8f32ec Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Wed, 7 Sep 2022 14:43:34 -0700 Subject: [PATCH 3/6] Allow existing Continue On users to skip using edit sessions Show an item to skip configuring edit sessions when triggering edit sessions via Continue On, and provide a setting to always skip configuring edit sessions when using Continue On --- .../browser/editSessions.contribution.ts | 63 ++++++++++++++++--- .../browser/editSessionsStorageService.ts | 44 +++++++------ .../editSessions/common/editSessions.ts | 1 + 3 files changed, 82 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts index 4733f13b6c4c0..5e3d4b098c6b0 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts @@ -29,7 +29,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; -import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { ContextKeyExpr, ContextKeyExpression, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -57,7 +57,7 @@ import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; registerSingleton(IEditSessionsLogService, EditSessionsLogService, false); registerSingleton(IEditSessionsStorageService, EditSessionsWorkbenchService, false); -const continueEditSessionCommand: IAction2Options = { +const continueWorkingOnCommand: IAction2Options = { id: '_workbench.experimental.editSessions.actions.continueEditSession', title: { value: localize('continue working on', "Continue Working On..."), original: 'Continue Working On...' }, precondition: WorkspaceFolderCountContext.notEqualsTo('0'), @@ -82,6 +82,7 @@ const resumingProgressOptions = { const queryParamName = 'editSessionId'; const experimentalSettingName = 'workbench.experimental.editSessions.enabled'; +const useEditSessionsWithContinueOn = 'workbench.experimental.editSessions.continueOn'; export class EditSessionsContribution extends Disposable implements IWorkbenchContribution { private registered = false; @@ -241,7 +242,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo const that = this; this._register(registerAction2(class ContinueEditSessionAction extends Action2 { constructor() { - super(continueEditSessionCommand); + super(continueWorkingOnCommand); } async run(accessor: ServicesAccessor, workspaceUri: URI | undefined): Promise { @@ -251,11 +252,16 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo }; that.telemetryService.publicLog2('editSessions.continue.store'); + const shouldStoreEditSession = await that.shouldContinueOnWithEditSession(); + let uri = workspaceUri ?? await that.pickContinueEditSessionDestination(); if (uri === undefined) { return; } // Run the store action to get back a ref - const ref = await that.storeEditSession(false); + let ref: string | undefined; + if (shouldStoreEditSession) { + ref = await that.storeEditSession(false); + } // Append the ref to the URI if (ref !== undefined && uri !== 'noDestinationUri') { @@ -267,8 +273,12 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo // Open the URI that.logService.info(`Opening ${uri.toString()}`); await that.openerService.open(uri, { openExternal: true }); - } else if (ref === undefined) { - that.logService.warn(`Failed to store edit session when invoking ${continueEditSessionCommand.id}.`); + } else if (!shouldStoreEditSession && uri !== 'noDestinationUri') { + // Open the URI without an edit session ref + that.logService.info(`Opening ${uri.toString()}`); + await that.openerService.open(uri, { openExternal: true }); + } else if (ref === undefined && shouldStoreEditSession) { + that.logService.warn(`Failed to store edit session when invoking ${continueWorkingOnCommand.id}.`); } } })); @@ -528,6 +538,34 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo return [...trackedUris]; } + private hasEditSession() { + for (const repository of this.scmService.repositories) { + if (this.getChangedResources(repository).length > 0) { + return true; + } + } + return false; + } + + private async shouldContinueOnWithEditSession(): Promise { + // If the user is already signed in, we should store edit session + if (this.editSessionsStorageService.isSignedIn) { + return true; + } + + // If the user has been asked before and said no, don't use edit sessions + if (this.configurationService.getValue(useEditSessionsWithContinueOn) === 'off') { + return false; + } + + // Prompt the user to use edit sessions if they currently could benefit from using it + if (this.hasEditSession()) { + return this.editSessionsStorageService.initialize(true); + } + + return false; + } + //#region Continue Edit Session extension contribution point private registerContributedEditSessionOptions() { @@ -689,7 +727,7 @@ const continueEditSessionExtPoint = ExtensionsRegistry.registerExtensionPoint(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(EditSessionsContribution, 'EditSessionsContribution', LifecyclePhase.Restored); -Registry.as(Extensions.Configuration).registerConfiguration({ +Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ ...workbenchConfigurationNodeBase, 'properties': { 'workbench.experimental.editSessions.autoStore': { @@ -720,5 +758,16 @@ Registry.as(Extensions.Configuration).registerConfigurat 'default': 'onReload', 'markdownDescription': localize('autoResume', "Controls whether to automatically resume an available edit session for the current workspace."), }, + 'workbench.experimental.editSessions.continueOn': { + enum: ['prompt', 'off'], + enumDescriptions: [ + localize('continueOn.promptForAuth', 'Prompt the user to sign in to store edit sessions with Continue Working On.'), + localize('continueOn.off', 'Do not use edit sessions with Continue Working On unless the user has already turned on edit sessions.') + ], + type: 'string', + tags: ['experimental', 'usesOnlineServices'], + default: 'prompt', + markdownDescription: localize('continueOn', 'Controls whether to prompt the user to store edit sessions when using Continue Working On.') + } } }); diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts index 080c82425bb4d..0bb2cdd7dc296 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts @@ -80,7 +80,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes * @returns The ref of the stored edit session state. */ async write(editSession: EditSession): Promise { - await this.initialize(); + await this.initialize(false); if (!this.initialized) { throw new Error('Please sign in to store your edit session.'); } @@ -95,7 +95,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes * @returns An object representing the requested or latest edit session state, if any. */ async read(ref: string | undefined): Promise<{ ref: string; editSession: EditSession } | undefined> { - await this.initialize(); + await this.initialize(false); if (!this.initialized) { throw new Error('Please sign in to apply your latest edit session.'); } @@ -119,7 +119,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes } async delete(ref: string | null) { - await this.initialize(); + await this.initialize(false); if (!this.initialized) { throw new Error(`Unable to delete edit session with ref ${ref}.`); } @@ -132,7 +132,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes } async list(): Promise { - await this.initialize(); + await this.initialize(false); if (!this.initialized) { throw new Error(`Unable to list edit sessions.`); } @@ -146,12 +146,14 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes return []; } - private async initialize() { + public async initialize(fromContinueOn: boolean) { if (this.initialized) { - return; + return true; } - this.initialized = await this.doInitialize(); + this.initialized = await this.doInitialize(fromContinueOn); this.signedInContext.set(this.initialized); + return this.initialized; + } /** @@ -160,7 +162,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes * meaning that authentication is configured and it * can be used to communicate with the remote storage service */ - private async doInitialize(): Promise { + private async doInitialize(fromContinueOn: boolean): Promise { // Wait for authentication extensions to be registered await this.extensionService.whenInstalledExtensionsRegistered(); @@ -181,7 +183,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes return true; } - const authenticationSession = await this.getAuthenticationSession(); + const authenticationSession = await this.getAuthenticationSession(fromContinueOn); if (authenticationSession !== undefined) { this.#authenticationInfo = authenticationSession; this.storeClient.setAuthToken(authenticationSession.token, authenticationSession.providerId); @@ -190,7 +192,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes return authenticationSession !== undefined; } - private async getAuthenticationSession() { + private async getAuthenticationSession(fromContinueOn: boolean) { // If the user signed in previously and the session is still available, reuse that without prompting the user again if (this.existingSessionId) { this.logService.info(`Searching for existing authentication session with ID ${this.existingSessionId}`); @@ -213,7 +215,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes } // Ask the user to pick a preferred account - const authenticationSession = await this.getAccountPreference(); + const authenticationSession = await this.getAccountPreference(fromContinueOn); if (authenticationSession !== undefined) { this.existingSessionId = authenticationSession.id; return { sessionId: authenticationSession.id, token: authenticationSession.idToken ?? authenticationSession.accessToken, providerId: authenticationSession.providerId }; @@ -230,13 +232,13 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes * * Prompts the user to pick an authentication option for storing and getting edit sessions. */ - private async getAccountPreference(): Promise { - const quickpick = this.quickInputService.createQuickPick(); - quickpick.title = localize('account preference', 'Sign In to Use Edit Sessions'); + private async getAccountPreference(fromContinueOn: boolean): Promise { + const quickpick = this.quickInputService.createQuickPick(); + quickpick.title = localize('account preference', 'Turn on Edit Sessions to bring your working changes with you'); quickpick.ok = false; quickpick.placeholder = localize('choose account placeholder', "Select an account to sign in"); quickpick.ignoreFocusOut = true; - quickpick.items = await this.createQuickpickItems(); + quickpick.items = await this.createQuickpickItems(fromContinueOn); return new Promise((resolve, reject) => { quickpick.onDidHide((e) => { @@ -246,7 +248,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes quickpick.onDidAccept(async (e) => { const selection = quickpick.selectedItems[0]; - const session = 'provider' in selection ? { ...await this.authenticationService.createSession(selection.provider.id, selection.provider.scopes), providerId: selection.provider.id } : selection.session; + const session = 'provider' in selection ? { ...await this.authenticationService.createSession(selection.provider.id, selection.provider.scopes), providerId: selection.provider.id } : ('session' in selection ? selection.session : undefined); resolve(session); quickpick.hide(); }); @@ -255,8 +257,8 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes }); } - private async createQuickpickItems(): Promise<(ExistingSession | AuthenticationProviderOption | IQuickPickSeparator)[]> { - const options: (ExistingSession | AuthenticationProviderOption | IQuickPickSeparator)[] = []; + private async createQuickpickItems(fromContinueOn: boolean): Promise<(ExistingSession | AuthenticationProviderOption | IQuickPickSeparator | IQuickPickItem & { canceledAuthentication: boolean })[]> { + const options: (ExistingSession | AuthenticationProviderOption | IQuickPickSeparator | IQuickPickItem & { canceledAuthentication: boolean })[] = []; options.push({ type: 'separator', label: localize('signed in', "Signed In") }); @@ -273,6 +275,10 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes } } + if (fromContinueOn) { + return options.concat([{ type: 'separator' }, { label: localize('continue without', 'Continue without my working changes'), canceledAuthentication: true }]); + } + return options; } @@ -399,7 +405,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes } async run() { - await that.initialize(); + return await that.initialize(false); } })); } diff --git a/src/vs/workbench/contrib/editSessions/common/editSessions.ts b/src/vs/workbench/contrib/editSessions/common/editSessions.ts index c6fc8fda976f0..99f664e11795d 100644 --- a/src/vs/workbench/contrib/editSessions/common/editSessions.ts +++ b/src/vs/workbench/contrib/editSessions/common/editSessions.ts @@ -24,6 +24,7 @@ export interface IEditSessionsStorageService { readonly isSignedIn: boolean; + initialize(fromContinueOn: boolean): Promise; read(ref: string | undefined): Promise<{ ref: string; editSession: EditSession } | undefined>; write(editSession: EditSession): Promise; delete(ref: string | null): Promise; From aa32b282f3e5971b29af962dfba5aa95f47e1963 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Wed, 7 Sep 2022 14:45:49 -0700 Subject: [PATCH 4/6] Sign Out -> Turn Off (aligning with settings sync) --- .../editSessions/browser/editSessionsStorageService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts index 0bb2cdd7dc296..ff4461f124938 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts @@ -416,7 +416,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes constructor() { super({ id: 'workbench.editSessions.actions.resetAuth', - title: localize('reset auth.v2', 'Sign Out of Edit Sessions'), + title: localize('reset auth.v3', 'Turn off Edit Sessions...'), category: EDIT_SESSION_SYNC_CATEGORY, precondition: ContextKeyExpr.equals(EDIT_SESSIONS_SIGNED_IN_KEY, true), menu: [{ @@ -433,8 +433,8 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes async run() { const result = await that.dialogService.confirm({ type: 'info', - message: localize('sign out of edit sessions clear data prompt', 'Do you want to sign out of edit sessions?'), - checkbox: { label: localize('delete all edit sessions', 'Delete all stored edit sessions from the cloud.') }, + message: localize('sign out of edit sessions clear data prompt.v2', 'Do you want to turn off Edit Sessions?'), + checkbox: { label: localize('delete all edit sessions.v2', 'Delete all stored data from the cloud.') }, primaryButton: localize('clear data confirm', 'Yes'), }); if (result.confirmed) { From 4a178fc184edeb0f2435afbec33a4a4c297004da Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Wed, 7 Sep 2022 15:09:31 -0700 Subject: [PATCH 5/6] Add quickpick item button to easily discover and configure setting to disable edit sessions with Continue On --- .../browser/editSessionsStorageService.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts index ff4461f124938..e557f402a9a6d 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts @@ -24,10 +24,13 @@ import { generateUuid } from 'vs/base/common/uuid'; import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { getCurrentAuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService'; import { isWeb } from 'vs/base/common/platform'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { Codicon } from 'vs/base/common/codicons'; type ExistingSession = IQuickPickItem & { session: AuthenticationSession & { providerId: string } }; type AuthenticationProviderOption = IQuickPickItem & { provider: IAuthenticationProvider }; +const configureContinueOnPreference = { iconClass: Codicon.settingsGear.classNames, tooltip: localize('configure continue on', 'Configure this preference in settings') }; export class EditSessionsWorkbenchService extends Disposable implements IEditSessionsStorageService { _serviceBrand = undefined; @@ -58,6 +61,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes @IRequestService private readonly requestService: IRequestService, @IDialogService private readonly dialogService: IDialogService, @ICredentialsService private readonly credentialsService: ICredentialsService, + @ICommandService private readonly commandService: ICommandService ) { super(); @@ -253,6 +257,12 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes quickpick.hide(); }); + quickpick.onDidTriggerItemButton(async (e) => { + if (e.button.tooltip === configureContinueOnPreference.tooltip) { + await this.commandService.executeCommand('workbench.action.openSettings', 'workbench.experimental.editSessions.continueOn'); + } + }); + quickpick.show(); }); } @@ -276,7 +286,11 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes } if (fromContinueOn) { - return options.concat([{ type: 'separator' }, { label: localize('continue without', 'Continue without my working changes'), canceledAuthentication: true }]); + return options.concat([{ type: 'separator' }, { + label: localize('continue without', 'Continue without my working changes'), + canceledAuthentication: true, + buttons: [configureContinueOnPreference] + }]); } return options; From ea9a7789b0684fc1e4861ef6031b1fa1da0a07d4 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Wed, 7 Sep 2022 16:22:55 -0700 Subject: [PATCH 6/6] Increment global activity badge if edit sessions aren't turned on --- .../browser/editSessions.contribution.ts | 4 +- .../browser/editSessionsStorageService.ts | 56 ++++++++++++++++--- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts index 5e3d4b098c6b0..40366891a2766 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts @@ -296,7 +296,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo }); } - async run(accessor: ServicesAccessor, editSessionId?: string): Promise { + async run(accessor: ServicesAccessor, editSessionId?: string, silent?: boolean): Promise { await that.progressService.withProgress(resumingProgressOptions, async () => { type ResumeEvent = {}; type ResumeClassification = { @@ -304,7 +304,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo }; that.telemetryService.publicLog2('editSessions.resume'); - await that.resumeEditSession(editSessionId); + await that.resumeEditSession(editSessionId, silent); }); } })); diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts index e557f402a9a6d..c47c73e27d381 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, MutableDisposable } 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'; @@ -26,11 +26,15 @@ import { getCurrentAuthenticationSessionInfo } from 'vs/workbench/services/authe import { isWeb } from 'vs/base/common/platform'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { Codicon } from 'vs/base/common/codicons'; +import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; +import { WorkspaceFolderCountContext } from 'vs/workbench/common/contextkeys'; type ExistingSession = IQuickPickItem & { session: AuthenticationSession & { providerId: string } }; type AuthenticationProviderOption = IQuickPickItem & { provider: IAuthenticationProvider }; const configureContinueOnPreference = { iconClass: Codicon.settingsGear.classNames, tooltip: localize('configure continue on', 'Configure this preference in settings') }; +const turnOnEditSessionsTitle = localize('sign in', 'Turn on Edit Sessions...'); + export class EditSessionsWorkbenchService extends Disposable implements IEditSessionsStorageService { _serviceBrand = undefined; @@ -48,6 +52,8 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes return this.existingSessionId !== undefined; } + private globalActivityBadgeDisposable = this._register(new MutableDisposable()); + constructor( @IFileService private readonly fileService: IFileService, @IStorageService private readonly storageService: IStorageService, @@ -61,7 +67,8 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes @IRequestService private readonly requestService: IRequestService, @IDialogService private readonly dialogService: IDialogService, @ICredentialsService private readonly credentialsService: ICredentialsService, - @ICommandService private readonly commandService: ICommandService + @ICommandService private readonly commandService: ICommandService, + @IActivityService private readonly activityService: IActivityService, ) { super(); @@ -71,11 +78,13 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes // If another window changes the preferred session storage, reset our cached auth state in memory this._register(this.storageService.onDidChangeValue(e => this.onDidChangeStorage(e))); - this.registerSignInAction(); + this.registerTurnOnAction(); this.registerResetAuthenticationAction(); this.signedInContext = EDIT_SESSIONS_SIGNED_IN.bindTo(this.contextKeyService); this.signedInContext.set(this.existingSessionId !== undefined); + + this.updateGlobalActivityBadge(); } /** @@ -156,6 +165,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes } this.initialized = await this.doInitialize(fromContinueOn); this.signedInContext.set(this.initialized); + this.updateGlobalActivityBadge(); return this.initialized; } @@ -398,13 +408,14 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes } } - private registerSignInAction() { + private registerTurnOnAction() { const that = this; - this._register(registerAction2(class ResetEditSessionAuthenticationAction extends Action2 { + const when = ContextKeyExpr.equals(EDIT_SESSIONS_SIGNED_IN_KEY, false); + this._register(registerAction2(class TurnOnEditSessionsAction extends Action2 { constructor() { super({ id: 'workbench.editSessions.actions.signIn', - title: localize('sign in', 'Turn on Edit Sessions...'), + title: turnOnEditSessionsTitle, category: EDIT_SESSION_SYNC_CATEGORY, precondition: ContextKeyExpr.equals(EDIT_SESSIONS_SIGNED_IN_KEY, false), menu: [{ @@ -413,7 +424,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes { id: MenuId.AccountsContext, group: '2_editSessions', - when: ContextKeyExpr.equals(EDIT_SESSIONS_SIGNED_IN_KEY, false), + when, }] }); } @@ -422,6 +433,28 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes return await that.initialize(false); } })); + + this._register(registerAction2(class TurnOnEditSessionsAndResumeAction extends Action2 { + constructor() { + super({ + id: 'workbench.editSessions.actions.turnOnAndResume', + title: turnOnEditSessionsTitle, + menu: { + group: '6_editSessions', + id: MenuId.GlobalActivity, + // Do not push for edit sessions when there are no workspace folders open + when: ContextKeyExpr.and(when, WorkspaceFolderCountContext.notEqualsTo(0)), + order: 2 + } + }); + } + + async run() { + if (await that.initialize(false)) { + await that.commandService.executeCommand('workbench.experimental.editSessions.actions.resumeLatest', undefined, true); + } + } + })); } private registerResetAuthenticationAction() { @@ -460,4 +493,13 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes } })); } + + private updateGlobalActivityBadge() { + if (this.initialized) { + return this.globalActivityBadgeDisposable.clear(); + } + + const badge = new NumberBadge(1, () => turnOnEditSessionsTitle); + this.globalActivityBadgeDisposable.value = this.activityService.showGlobalActivity({ badge, priority: 1 }); + } }