diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 9c222a0048e..48b3639196b 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -502,19 +502,6 @@ class WindowIndicator implements IWindowIndicator { // settings sync options const settingsSyncOptions: ISettingsSyncOptions | undefined = config.settingsSyncOptions ? { enabled: config.settingsSyncOptions.enabled, - enablementHandler: (enablement) => { - let queryString = `settingsSync=${enablement ? 'true' : 'false'}`; - - // Save all other query params we might have - const query = new URL(document.location.href).searchParams; - query.forEach((value, key) => { - if (key !== 'settingsSync') { - queryString += `&${key}=${value}`; - } - }); - - window.location.href = `${window.location.origin}?${queryString}`; - } } : undefined; // Finally create workbench diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 0c8e6a589d8..de7ee8b6962 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -56,6 +56,7 @@ import { Codicon } from 'vs/base/common/codicons'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { EditorResolution } from 'vs/platform/editor/common/editor'; import { CATEGORIES } from 'vs/workbench/common/actions'; +import { IUserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit'; const CONTEXT_CONFLICTS_SOURCES = new RawContextKey('conflictsSources', ''); @@ -88,10 +89,12 @@ const syncNowCommand = { const showSyncSettingsCommand = { id: 'workbench.userDataSync.actions.settings', title: localize('sync settings', "{0}: Show Settings", SYNC_TITLE), }; const showSyncedDataCommand = { id: 'workbench.userDataSync.actions.showSyncedData', title: localize('show synced data', "{0}: Show Synced Data", SYNC_TITLE), }; +const CONTEXT_SYNC_AFTER_INITIALIZATION = new RawContextKey('syncAfterInitialization', false); const CONTEXT_TURNING_ON_STATE = new RawContextKey('userDataSyncTurningOn', false); export class UserDataSyncWorkbenchContribution extends Disposable implements IWorkbenchContribution { + private readonly syncAfterInitializationContext: IContextKey; private readonly turningOnSyncContext: IContextKey; private readonly conflictsSources: IContextKey; @@ -123,15 +126,18 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo @IAuthenticationService private readonly authenticationService: IAuthenticationService, @IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, @IConfigurationService private readonly configurationService: IConfigurationService, + @IUserDataInitializationService private readonly userDataInitializationService: IUserDataInitializationService, ) { super(); + this.syncAfterInitializationContext = CONTEXT_SYNC_AFTER_INITIALIZATION.bindTo(contextKeyService); this.turningOnSyncContext = CONTEXT_TURNING_ON_STATE.bindTo(contextKeyService); this.conflictsSources = CONTEXT_CONFLICTS_SOURCES.bindTo(contextKeyService); if (userDataSyncWorkbenchService.enabled) { registerConfiguration(); + this.initializeSyncAfterInitializationContext(); this.updateAccountBadge(); this.updateGlobalActivityBadge(); this.onDidChangeConflicts(this.userDataSyncService.conflicts); @@ -169,6 +175,27 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo this.updateGlobalActivityBadge(); } + private async initializeSyncAfterInitializationContext(): Promise { + const requiresInitialization = await this.userDataInitializationService.requiresInitialization(); + if (requiresInitialization) { + this.updateSyncAfterInitializationContext(true); + } else { + this.updateSyncAfterInitializationContext(this.storageService.getBoolean(CONTEXT_SYNC_AFTER_INITIALIZATION.key, StorageScope.GLOBAL, false)); + } + const disposable = this._register(this.userDataAutoSyncEnablementService.onDidChangeEnablement(() => { + if (this.userDataAutoSyncEnablementService.isEnabled()) { + this.updateSyncAfterInitializationContext(false); + disposable.dispose(); + } + })); + } + + private async updateSyncAfterInitializationContext(value: boolean): Promise { + this.storageService.store(CONTEXT_SYNC_AFTER_INITIALIZATION.key, value, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.syncAfterInitializationContext.set(value); + this.updateGlobalActivityBadge(); + } + private readonly conflictsDisposables = new Map(); private onDidChangeConflicts(conflicts: [SyncResource, IResourcePreview[]][]) { if (!this.userDataAutoSyncEnablementService.isEnabled()) { @@ -434,6 +461,8 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo badge = new ProgressBadge(() => localize('turning on syncing', "Turning on Settings Sync...")); clazz = 'progress-badge'; priority = 1; + } else if (this.userDataSyncWorkbenchService.accountStatus === AccountStatus.Available && this.syncAfterInitializationContext.get()) { + badge = new NumberBadge(1, () => localize('settings sync is off', "Settings Sync is Off", SYNC_TITLE)); } if (badge) { @@ -455,6 +484,24 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } } + private async turnOnSyncAfterInitialization(): Promise { + this.updateSyncAfterInitializationContext(false); + const result = await this.dialogService.show( + Severity.Info, + localize('turnon sync after initialization message', "Your settings, keybindings, extensions, snippets and UI State were initialized but are not getting synced. Do you want to turn on Settings Sync?"), + [ + localize('turn on settings sync', "Turn On Settings Sync"), + localize('cancel', "Cancel"), + ], + { + cancelId: 1 + } + ); + if (result.choice === 0) { + await this.userDataSyncWorkbenchService.turnOnUsingCurrentAccount(); + } + } + private async turnOn(): Promise { try { if (!this.userDataSyncWorkbenchService.authenticationProviders.length) { @@ -468,7 +515,6 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo await this.selectSettingsSyncService(this.userDataSyncStoreManagementService.userDataSyncStore); } await this.userDataSyncWorkbenchService.turnOn(); - this.storageService.store('sync.donotAskPreviewConfirmation', true, StorageScope.GLOBAL, StorageTarget.MACHINE); } catch (e) { if (isPromiseCanceledError(e)) { return; @@ -725,8 +771,9 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo if (this.userDataAutoSyncEnablementService.canToggleEnablement()) { this.registerTurnOnSyncAction(); this.registerTurnOffSyncAction(); + this.registerTurnOnSyncAfterInitializationAction(); } - this.registerTurninOnSyncAction(); + this.registerTurningOnSyncAction(); this.registerSignInAction(); // When Sync is turned on from CLI this.registerShowSettingsConflictsAction(); this.registerShowKeybindingsConflictsAction(); @@ -751,7 +798,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo id: turnOnSyncCommand.id, title: localize('global activity turn on sync', "Turn on Settings Sync...") }, - when: turnOnSyncWhenContext, + when: ContextKeyExpr.and(turnOnSyncWhenContext, CONTEXT_SYNC_AFTER_INITIALIZATION.negate()), order: 1 }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { @@ -776,7 +823,34 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo }); } - private registerTurninOnSyncAction(): void { + private registerTurnOnSyncAfterInitializationAction(): void { + const that = this; + const id = 'workbench.userData.actions.askToTunrOnAfterInit'; + const when = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT.toNegated(), CONTEXT_ACCOUNT_STATE.isEqualTo(AccountStatus.Available), CONTEXT_TURNING_ON_STATE.negate(), CONTEXT_SYNC_AFTER_INITIALIZATION); + this._register(registerAction2(class AskToTurnOnSync extends Action2 { + constructor() { + super({ + id, + title: localize('ask to turn on in global', "Settings Sync is Off (1)"), + menu: { + group: '5_sync', + id: MenuId.GlobalActivity, + when, + order: 2 + } + }); + } + async run(): Promise { + try { + await that.turnOnSyncAfterInitialization(); + } catch (e) { + that.notificationService.error(e); + } + } + })); + } + + private registerTurningOnSyncAction(): void { const when = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT.toNegated(), CONTEXT_ACCOUNT_STATE.notEqualsTo(AccountStatus.Uninitialized), CONTEXT_TURNING_ON_STATE); this._register(registerAction2(class TurningOnSyncAction extends Action2 { constructor() { diff --git a/src/vs/workbench/services/userData/browser/userDataInit.ts b/src/vs/workbench/services/userData/browser/userDataInit.ts index 8919616276f..3038d092d82 100644 --- a/src/vs/workbench/services/userData/browser/userDataInit.ts +++ b/src/vs/workbench/services/userData/browser/userDataInit.ts @@ -73,49 +73,63 @@ export class UserDataInitializationService implements IUserDataInitializationSer private createUserDataSyncStoreClient(): Promise { if (!this._userDataSyncStoreClientPromise) { this._userDataSyncStoreClientPromise = (async (): Promise => { - if (!isWeb) { - this.logService.trace(`Skipping initializing user data in desktop`); - return; - } - - if (!this.storageService.isNew(StorageScope.GLOBAL)) { - this.logService.trace(`Skipping initializing user data as application was opened before`); - return; - } - - if (!this.storageService.isNew(StorageScope.WORKSPACE)) { - this.logService.trace(`Skipping initializing user data as workspace was opened before`); - return; - } - - if (!this.environmentService.options?.credentialsProvider) { - this.logService.trace(`Skipping initializing user data as credentials provider is not provided`); - return; - } - - let authenticationSession; try { - authenticationSession = await getCurrentAuthenticationSessionInfo(this.environmentService, this.productService); + if (!isWeb) { + this.logService.trace(`Skipping initializing user data in desktop`); + return; + } + + if (!this.storageService.isNew(StorageScope.GLOBAL)) { + this.logService.trace(`Skipping initializing user data as application was opened before`); + return; + } + + if (!this.storageService.isNew(StorageScope.WORKSPACE)) { + this.logService.trace(`Skipping initializing user data as workspace was opened before`); + return; + } + + if (!this.environmentService.options?.credentialsProvider) { + this.logService.trace(`Skipping initializing user data as credentials provider is not provided`); + return; + } + + let authenticationSession; + try { + authenticationSession = await getCurrentAuthenticationSessionInfo(this.environmentService, this.productService); + } catch (error) { + this.logService.error(error); + } + if (!authenticationSession) { + this.logService.trace(`Skipping initializing user data as authentication session is not set`); + return; + } + + await this.initializeUserDataSyncStore(authenticationSession); + + const userDataSyncStore = this.userDataSyncStoreManagementService.userDataSyncStore; + if (!userDataSyncStore) { + this.logService.trace(`Skipping initializing user data as sync service is not provided`); + return; + } + + const userDataSyncStoreClient = new UserDataSyncStoreClient(userDataSyncStore.url, this.productService, this.requestService, this.logService, this.environmentService, this.fileService, this.storageService); + userDataSyncStoreClient.setAuthToken(authenticationSession.accessToken, authenticationSession.providerId); + + const manifest = await userDataSyncStoreClient.manifest(null); + if (manifest === null) { + userDataSyncStoreClient.dispose(); + this.logService.trace(`Skipping initializing user data as there is no data`); + return; + } + + this.logService.info(`Using settings sync service ${userDataSyncStore.url.toString()} for initialization`); + return userDataSyncStoreClient; + } catch (error) { this.logService.error(error); - } - if (!authenticationSession) { - this.logService.trace(`Skipping initializing user data as authentication session is not set`); return; } - - await this.initializeUserDataSyncStore(authenticationSession); - - const userDataSyncStore = this.userDataSyncStoreManagementService.userDataSyncStore; - if (!userDataSyncStore) { - this.logService.trace(`Skipping initializing user data as sync service is not provided`); - return; - } - - this.logService.info(`Using settings sync service ${userDataSyncStore.url.toString()} for initialization`); - const userDataSyncStoreClient = new UserDataSyncStoreClient(userDataSyncStore.url, this.productService, this.requestService, this.logService, this.environmentService, this.fileService, this.storageService); - userDataSyncStoreClient.setAuthToken(authenticationSession.accessToken, authenticationSession.providerId); - return userDataSyncStoreClient; })(); } diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index 7796a8a328e..a2b8a6bb0ac 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -276,6 +276,22 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat throw new Error(localize('no account', "No account available")); } + await this.turnOnUsingCurrentAccount(); + } + + async turnOnUsingCurrentAccount(): Promise { + if (this.userDataAutoSyncEnablementService.isEnabled()) { + return; + } + + if (this.userDataSyncService.status !== SyncStatus.Idle) { + throw new Error('Cannont turn on sync while syncing'); + } + + if (this.accountStatus !== AccountStatus.Available) { + throw new Error(localize('no account', "No account available")); + } + const syncTitle = SYNC_TITLE; const title = `${syncTitle} [(${localize('show log', "show log")})](command:${SHOW_SYNC_LOG_COMMAND_ID})`; const manualSyncTask = await this.userDataSyncService.createManualSyncTask(); diff --git a/src/vs/workbench/services/userDataSync/common/userDataSync.ts b/src/vs/workbench/services/userDataSync/common/userDataSync.ts index c40f3fa8966..fcf13eae627 100644 --- a/src/vs/workbench/services/userDataSync/common/userDataSync.ts +++ b/src/vs/workbench/services/userDataSync/common/userDataSync.ts @@ -58,6 +58,7 @@ export interface IUserDataSyncWorkbenchService { readonly userDataSyncPreview: IUserDataSyncPreview; turnOn(): Promise; + turnOnUsingCurrentAccount(): Promise; turnoff(everyWhere: boolean): Promise; signIn(): Promise;