Skip to content

Commit 1dd6759

Browse files
authored
Ask for edit sessions auth when coming from Continue On (microsoft#160843)
* Ask for edit sessions auth when coming from Continue On * Only append `continueOn` query param if there is an edit session * Persist continueOn for new windows * Don't double prompt to sign in * Add badge if user didn't sign in * Add logging and dev commands * Log more info * More logging * 🤔 * Cleanup * Actually check for pending edit sessions * More cleanup * Update test
1 parent e7cfaa6 commit 1dd6759

File tree

11 files changed

+83
-4
lines changed

11 files changed

+83
-4
lines changed

src/vs/code/electron-main/app.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,14 @@ export class CodeApplication extends Disposable {
918918
// or if no window is open (macOS only)
919919
shouldOpenInNewWindow ||= isMacintosh && windowsMainService.getWindowCount() === 0;
920920

921+
// Pass along whether the application is being opened via a Continue On flow
922+
const continueOn = params.get('continueOn');
923+
if (continueOn !== null) {
924+
environmentService.continueOn = continueOn ?? undefined;
925+
params.delete('continueOn');
926+
uri = uri.with({ query: params.toString() });
927+
}
928+
921929
// Check for URIs to open in window
922930
const windowOpenableFromProtocolLink = app.getWindowOpenableFromProtocolLink(uri);
923931
logService.trace('app#handleURL: windowOpenableFromProtocolLink = ', windowOpenableFromProtocolLink);

src/vs/platform/environment/common/argv.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ export interface NativeParsedArgs {
9090
'logsPath'?: string;
9191
'__enable-file-policy'?: boolean;
9292
editSessionId?: string;
93+
continueOn?: string;
9394
'locate-shell-integration-path'?: string;
9495
'profile'?: string;
9596
'profile-temp'?: boolean;

src/vs/platform/environment/common/environment.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export interface IEnvironmentService {
6565
sync: 'on' | 'off' | undefined;
6666

6767
// --- continue edit session
68+
continueOn?: string;
6869
editSessionId?: string;
6970
editSessionsLogResource: URI;
7071

src/vs/platform/environment/common/environmentService.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,14 @@ export abstract class AbstractNativeEnvironmentService implements INativeEnviron
247247

248248
editSessionId: string | undefined = this.args['editSessionId'];
249249

250+
get continueOn(): string | undefined {
251+
return this.args['continueOn'];
252+
}
253+
254+
set continueOn(value: string | undefined) {
255+
this.args['continueOn'] = value;
256+
}
257+
250258
get args(): NativeParsedArgs { return this._args; }
251259

252260
constructor(

src/vs/platform/environment/node/argv.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ export const OPTIONS: OptionDescriptions<Required<NativeParsedArgs>> = {
129129
'logsPath': { type: 'string' },
130130
'__enable-file-policy': { type: 'boolean' },
131131
'editSessionId': { type: 'string' },
132+
'continueOn': { type: 'string' },
132133
'locate-shell-integration-path': { type: 'string', args: ['bash', 'pwsh', 'zsh', 'fish'] },
133134

134135
'enable-coi': { type: 'boolean' },

src/vs/platform/windows/electron-main/windowImpl.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
973973

974974
configuration.isInitialStartup = false; // since this is a reload
975975
configuration.policiesData = this.policyService.serialize(); // set policies data again
976+
configuration.continueOn = this.environmentMainService.continueOn;
976977
configuration.profiles = {
977978
all: this.userDataProfilesService.profiles,
978979
profile: this.profile || this.userDataProfilesService.defaultProfile

src/vs/platform/windows/electron-main/windowsMainService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,6 +1367,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
13671367
accessibilitySupport: app.accessibilitySupportEnabled,
13681368
colorScheme: this.themeMainService.getColorScheme(),
13691369
policiesData: this.policyService.serialize(),
1370+
continueOn: this.environmentMainService.continueOn,
13701371
};
13711372

13721373
let window: ICodeWindow | undefined;

src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { Disposable } from 'vs/base/common/lifecycle';
6+
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
77
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
88
import { Registry } from 'vs/platform/registry/common/platform';
99
import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
@@ -54,6 +54,8 @@ import { ThemeIcon } from 'vs/platform/theme/common/themeService';
5454
import { IOutputService } from 'vs/workbench/services/output/common/output';
5555
import * as Constants from 'vs/workbench/contrib/logs/common/logConstants';
5656
import { sha1Hex } from 'vs/base/browser/hash';
57+
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
58+
import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity';
5759

5860
registerSingleton(IEditSessionsLogService, EditSessionsLogService, false);
5961
registerSingleton(IEditSessionsStorageService, EditSessionsWorkbenchService, false);
@@ -89,6 +91,9 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
8991

9092
private readonly shouldShowViewsContext: IContextKey<boolean>;
9193

94+
private static APPLICATION_LAUNCHED_VIA_CONTINUE_ON_STORAGE_KEY = 'applicationLaunchedViaContinueOn';
95+
private accountsMenuBadgeDisposable = this._register(new MutableDisposable());
96+
9297
constructor(
9398
@IEditSessionsStorageService private readonly editSessionsStorageService: IEditSessionsStorageService,
9499
@IFileService private readonly fileService: IFileService,
@@ -109,7 +114,9 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
109114
@ICommandService private commandService: ICommandService,
110115
@IContextKeyService private readonly contextKeyService: IContextKeyService,
111116
@IFileDialogService private readonly fileDialogService: IFileDialogService,
112-
@ILifecycleService private readonly lifecycleService: ILifecycleService
117+
@ILifecycleService private readonly lifecycleService: ILifecycleService,
118+
@IStorageService private readonly storageService: IStorageService,
119+
@IActivityService private readonly activityService: IActivityService,
113120
) {
114121
super();
115122

@@ -123,6 +130,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
123130

124131
this._register(this.fileService.registerProvider(EditSessionsFileSystemProvider.SCHEMA, new EditSessionsFileSystemProvider(this.editSessionsStorageService)));
125132
this.lifecycleService.onWillShutdown((e) => e.join(this.autoStoreEditSession(), { id: 'autoStoreEditSession', label: localize('autoStoreEditSession', 'Storing current edit session...') }));
133+
this._register(this.editSessionsStorageService.onDidSignIn(() => this.updateAccountsMenuBadge()));
126134
}
127135

128136
private autoResumeEditSession() {
@@ -136,21 +144,58 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
136144
this.telemetryService.publicLog2<ResumeEvent, ResumeClassification>('editSessions.continue.resume');
137145

138146
if (this.environmentService.editSessionId !== undefined) {
147+
this.logService.info(`Resuming edit session, reason: found editSessionId ${this.environmentService.editSessionId} in environment service...`);
139148
await this.resumeEditSession(this.environmentService.editSessionId).finally(() => this.environmentService.editSessionId = undefined);
140149
} else if (
141150
this.configurationService.getValue('workbench.editSessions.autoResume') === 'onReload' &&
142151
this.editSessionsStorageService.isSignedIn
143152
) {
153+
this.logService.info('Resuming edit session, reason: edit sessions enabled...');
144154
// Attempt to resume edit session based on edit workspace identifier
145155
// Note: at this point if the user is not signed into edit sessions,
146156
// we don't want them to be prompted to sign in and should just return early
147157
await this.resumeEditSession(undefined, true);
158+
} else {
159+
// The application has previously launched via a protocol URL Continue On flow
160+
const hasApplicationLaunchedFromContinueOnFlow = this.storageService.getBoolean(EditSessionsContribution.APPLICATION_LAUNCHED_VIA_CONTINUE_ON_STORAGE_KEY, StorageScope.APPLICATION, false);
161+
162+
if ((this.environmentService.continueOn !== undefined) &&
163+
!this.editSessionsStorageService.isSignedIn &&
164+
// and user has not yet been prompted to sign in on this machine
165+
hasApplicationLaunchedFromContinueOnFlow === false
166+
) {
167+
this.storageService.store(EditSessionsContribution.APPLICATION_LAUNCHED_VIA_CONTINUE_ON_STORAGE_KEY, true, StorageScope.APPLICATION, StorageTarget.MACHINE);
168+
await this.editSessionsStorageService.initialize(true);
169+
if (this.editSessionsStorageService.isSignedIn) {
170+
await this.resumeEditSession(undefined, true);
171+
} else {
172+
this.updateAccountsMenuBadge();
173+
}
174+
// store the fact that we prompted the user
175+
} else if (!this.editSessionsStorageService.isSignedIn &&
176+
// and user has been prompted to sign in on this machine
177+
hasApplicationLaunchedFromContinueOnFlow === true
178+
) {
179+
// display a badge in the accounts menu but do not prompt the user to sign in again
180+
this.updateAccountsMenuBadge();
181+
// attempt a resume if we are in a pending state and the user just signed in
182+
this._register(this.editSessionsStorageService.onDidSignIn(async () => this.resumeEditSession(undefined, true)));
183+
}
148184
}
149185

150186
performance.mark('code/didResumeEditSessionFromIdentifier');
151187
});
152188
}
153189

190+
private updateAccountsMenuBadge() {
191+
if (this.editSessionsStorageService.isSignedIn) {
192+
return this.accountsMenuBadgeDisposable.clear();
193+
}
194+
195+
const badge = new NumberBadge(1, () => localize('check for pending edit sessions', 'Check for pending edit sessions'));
196+
this.accountsMenuBadgeDisposable.value = this.activityService.showAccountsActivity({ badge, priority: 1 });
197+
}
198+
154199
private async autoStoreEditSession() {
155200
if (this.configurationService.getValue('workbench.experimental.editSessions.autoStore') === 'onShutdown') {
156201
await this.progressService.withProgress({
@@ -252,7 +297,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
252297
if (ref !== undefined && uri !== 'noDestinationUri') {
253298
const encodedRef = encodeURIComponent(ref);
254299
uri = uri.with({
255-
query: uri.query.length > 0 ? (uri.query + `&${queryParamName}=${encodedRef}`) : `${queryParamName}=${encodedRef}`
300+
query: uri.query.length > 0 ? (uri.query + `&${queryParamName}=${encodedRef}&continueOn=1`) : `${queryParamName}=${encodedRef}&continueOn=1`
256301
});
257302

258303
// Open the URI

src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { isWeb } from 'vs/base/common/platform';
2727
import { ICommandService } from 'vs/platform/commands/common/commands';
2828
import { Codicon } from 'vs/base/common/codicons';
2929
import { IUserDataSyncMachinesService, UserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines';
30+
import { Emitter } from 'vs/base/common/event';
3031

3132
type ExistingSession = IQuickPickItem & { session: AuthenticationSession & { providerId: string } };
3233
type AuthenticationProviderOption = IQuickPickItem & { provider: IAuthenticationProvider };
@@ -50,6 +51,11 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes
5051
return this.existingSessionId !== undefined;
5152
}
5253

54+
private _didSignIn = new Emitter<void>();
55+
get onDidSignIn() {
56+
return this._didSignIn.event;
57+
}
58+
5359
constructor(
5460
@IFileService private readonly fileService: IFileService,
5561
@IStorageService private readonly storageService: IStorageService,
@@ -162,6 +168,9 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes
162168
}
163169
this.initialized = await this.doInitialize(fromContinueOn);
164170
this.signedInContext.set(this.initialized);
171+
if (this.initialized) {
172+
this._didSignIn.fire();
173+
}
165174
return this.initialized;
166175

167176
}

src/vs/workbench/contrib/editSessions/common/editSessions.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
1212
import { ILogService } from 'vs/platform/log/common/log';
1313
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
1414
import { IResourceRefHandle } from 'vs/platform/userDataSync/common/userDataSync';
15+
import { Event } from 'vs/base/common/event';
1516

1617
export const EDIT_SESSION_SYNC_CATEGORY: ILocalizedString = {
1718
original: 'Edit Sessions',
@@ -23,6 +24,7 @@ export interface IEditSessionsStorageService {
2324
_serviceBrand: undefined;
2425

2526
readonly isSignedIn: boolean;
27+
readonly onDidSignIn: Event<void>;
2628

2729
initialize(fromContinueOn: boolean): Promise<boolean>;
2830
read(ref: string | undefined): Promise<{ ref: string; editSession: EditSession } | undefined>;

0 commit comments

Comments
 (0)