33 * Licensed under the MIT License. See License.txt in the project root for license information.
44 *--------------------------------------------------------------------------------------------*/
55
6- import { Delayer , disposableTimeout , CancelablePromise , createCancelablePromise } from 'vs/base/common/async' ;
6+ import { Delayer , disposableTimeout , CancelablePromise , createCancelablePromise , timeout } from 'vs/base/common/async' ;
77import { Event , Emitter } from 'vs/base/common/event' ;
88import { Disposable , toDisposable , MutableDisposable , IDisposable } from 'vs/base/common/lifecycle' ;
99import { IUserDataSyncLogService , IUserDataSyncService , IUserDataAutoSyncService , UserDataSyncError , UserDataSyncErrorCode , IUserDataSyncResourceEnablementService , IUserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSync' ;
@@ -13,6 +13,10 @@ import { isPromiseCanceledError } from 'vs/base/common/errors';
1313import { CancellationToken } from 'vs/base/common/cancellation' ;
1414import { IStorageService , StorageScope , IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage' ;
1515import { IEnvironmentService } from 'vs/platform/environment/common/environment' ;
16+ import { IUserDataSyncMachine , IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines' ;
17+ import { PlatformToString , isWeb , Platform , platform } from 'vs/base/common/platform' ;
18+ import { escapeRegExpCharacters } from 'vs/base/common/strings' ;
19+ import { IProductService } from 'vs/platform/product/common/productService' ;
1620
1721type AutoSyncClassification = {
1822 sources : { classification : 'SystemMetaData' , purpose : 'FeatureInsight' , isMeasurement : true } ;
@@ -23,6 +27,7 @@ type AutoSyncEnablementClassification = {
2327} ;
2428
2529const enablementKey = 'sync.enable' ;
30+ const disableMachineEventuallyKey = 'sync.disableMachineEventually' ;
2631
2732export class UserDataAutoSyncEnablementService extends Disposable {
2833
@@ -80,6 +85,8 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
8085 @IUserDataSyncLogService private readonly logService : IUserDataSyncLogService ,
8186 @IUserDataSyncAccountService private readonly authTokenService : IUserDataSyncAccountService ,
8287 @ITelemetryService private readonly telemetryService : ITelemetryService ,
88+ @IUserDataSyncMachinesService private readonly userDataSyncMachinesService : IUserDataSyncMachinesService ,
89+ @IProductService private readonly productService : IProductService ,
8390 @IStorageService storageService : IStorageService ,
8491 @IEnvironmentService environmentService : IEnvironmentService
8592 ) {
@@ -88,6 +95,14 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
8895
8996 if ( userDataSyncStoreService . userDataSyncStore ) {
9097 this . updateAutoSync ( ) ;
98+
99+ // Update machine if sync is enabled
100+ if ( this . isEnabled ( ) ) {
101+ this . updateMachine ( true ) ;
102+ } else if ( this . hasToDisableMachineEventually ( ) ) {
103+ this . disableMachineEventually ( ) ;
104+ }
105+
91106 this . _register ( authTokenService . onDidChangeAccount ( ( ) => this . updateAutoSync ( ) ) ) ;
92107 this . _register ( Event . debounce < string , string [ ] > ( userDataSyncService . onDidChangeLocal , ( last , source ) => last ? [ ...last , source ] : [ source ] , 1000 ) ( sources => this . triggerSync ( sources , false ) ) ) ;
93108 this . _register ( Event . filter ( this . userDataSyncResourceEnablementService . onDidChangeResourceEnablement , ( [ , enabled ] ) => enabled ) ( ( ) => this . triggerSync ( [ 'resourceEnablement' ] , false ) ) ) ;
@@ -128,6 +143,8 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
128143 }
129144
130145 async turnOn ( pullFirst : boolean ) : Promise < void > {
146+ await this . updateMachine ( true ) ;
147+
131148 if ( pullFirst ) {
132149 await this . userDataSyncService . pull ( ) ;
133150 } else {
@@ -137,8 +154,27 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
137154 this . setEnablement ( true ) ;
138155 }
139156
140- async turnOff ( ) : Promise < void > {
141- this . setEnablement ( false ) ;
157+ async turnOff ( everywhere : boolean , softTurnOffOnError ?: boolean , donotDisableMachine ?: boolean ) : Promise < void > {
158+ try {
159+ if ( ! donotDisableMachine ) {
160+ await this . updateMachine ( false ) ;
161+ }
162+ this . setEnablement ( false ) ;
163+
164+ if ( everywhere ) {
165+ this . telemetryService . publicLog2 ( 'sync/turnOffEveryWhere' ) ;
166+ await this . userDataSyncService . reset ( ) ;
167+ } else {
168+ await this . userDataSyncService . resetLocal ( ) ;
169+ }
170+ } catch ( error ) {
171+ if ( softTurnOffOnError ) {
172+ this . logService . error ( error ) ;
173+ this . setEnablement ( false ) ;
174+ } else {
175+ throw error ;
176+ }
177+ }
142178 }
143179
144180 private setEnablement ( enabled : boolean ) : void {
@@ -149,6 +185,44 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
149185 }
150186 }
151187
188+ private async updateMachine ( enable : boolean ) : Promise < void > {
189+ if ( ! this . authTokenService . account ) {
190+ return ;
191+ }
192+
193+ const machines = await this . userDataSyncMachinesService . getMachines ( ) ;
194+ const currentMachine = machines . find ( machine => machine . isCurrent ) ;
195+ if ( enable ) {
196+ this . stopDisableMachineEventually ( ) ;
197+ // Add or enable current machine
198+ if ( ! currentMachine ) {
199+ const name = this . computeDefaultMachineName ( machines ) ;
200+ await this . userDataSyncMachinesService . addCurrentMachine ( name ) ;
201+ this . logService . debug ( 'Auto Sync: Added current machine to sync' ) ;
202+ } else if ( currentMachine . disabled ) {
203+ await this . userDataSyncMachinesService . setEnablement ( currentMachine . id , true ) ;
204+ this . logService . debug ( 'Auto Sync: Enabled current machine to sync' ) ;
205+ }
206+ } else if ( currentMachine && ! currentMachine . disabled ) {
207+ await this . userDataSyncMachinesService . setEnablement ( currentMachine . id , false ) ;
208+ this . logService . debug ( 'Auto Sync: Disabled current machine to sync' ) ;
209+ }
210+ }
211+
212+ private computeDefaultMachineName ( machines : IUserDataSyncMachine [ ] ) : string {
213+ const namePrefix = `${ this . productService . nameLong } (${ PlatformToString ( isWeb ? Platform . Web : platform ) } )` ;
214+ const nameRegEx = new RegExp ( `${ escapeRegExpCharacters ( namePrefix ) } \\s#(\\d)` ) ;
215+
216+ let nameIndex = 0 ;
217+ for ( const machine of machines ) {
218+ const matches = nameRegEx . exec ( machine . name ) ;
219+ const index = matches ? parseInt ( matches [ 1 ] ) : 0 ;
220+ nameIndex = index > nameIndex ? index : nameIndex ;
221+ }
222+
223+ return `${ namePrefix } #${ nameIndex + 1 } ` ;
224+ }
225+
152226 private async onDidFinishSync ( error : Error | undefined ) : Promise < void > {
153227 if ( ! error ) {
154228 // Sync finished without errors
@@ -159,12 +233,12 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
159233 // Error while syncing
160234 const userDataSyncError = UserDataSyncError . toUserDataSyncError ( error ) ;
161235 if ( userDataSyncError . code === UserDataSyncErrorCode . TurnedOff || userDataSyncError . code === UserDataSyncErrorCode . SessionExpired ) {
162- this . logService . info ( 'Auto Sync: Sync is turned off in the cloud.' ) ;
163- await this . userDataSyncService . resetLocal ( ) ;
164- this . turnOff ( ) ;
236+ await this . turnOff ( false , true /* force soft turnoff on error */ ) ;
165237 this . logService . info ( 'Auto Sync: Turned off sync because sync is turned off in the cloud' ) ;
166- } else if ( userDataSyncError . code === UserDataSyncErrorCode . LocalTooManyRequests ) {
167- this . turnOff ( ) ;
238+ } else if ( userDataSyncError . code === UserDataSyncErrorCode . LocalTooManyRequests || userDataSyncError . code === UserDataSyncErrorCode . TooManyRequests ) {
239+ await this . turnOff ( false , true /* force soft turnoff on error */ ,
240+ true /* do not disable machine because disabling a machine makes request to server and can fail with TooManyRequests */ ) ;
241+ this . disableMachineEventually ( ) ;
168242 this . logService . info ( 'Auto Sync: Turned off sync because of making too many requests to server' ) ;
169243 } else {
170244 this . logService . error ( userDataSyncError ) ;
@@ -173,6 +247,31 @@ export class UserDataAutoSyncService extends UserDataAutoSyncEnablementService i
173247 this . _onError . fire ( userDataSyncError ) ;
174248 }
175249
250+ private async disableMachineEventually ( ) : Promise < void > {
251+ this . storageService . store ( disableMachineEventuallyKey , true , StorageScope . GLOBAL ) ;
252+ await timeout ( 1000 * 60 * 10 ) ;
253+
254+ // Return if got stopped meanwhile.
255+ if ( ! this . hasToDisableMachineEventually ( ) ) {
256+ return ;
257+ }
258+
259+ this . stopDisableMachineEventually ( ) ;
260+
261+ // disable only if sync is disabled
262+ if ( ! this . isEnabled ( ) ) {
263+ return this . updateMachine ( false ) ;
264+ }
265+ }
266+
267+ private hasToDisableMachineEventually ( ) : boolean {
268+ return this . storageService . getBoolean ( disableMachineEventuallyKey , StorageScope . GLOBAL , false ) ;
269+ }
270+
271+ private stopDisableMachineEventually ( ) : void {
272+ this . storageService . remove ( disableMachineEventuallyKey , StorageScope . GLOBAL ) ;
273+ }
274+
176275 private sources : string [ ] = [ ] ;
177276 async triggerSync ( sources : string [ ] , skipIfSyncedRecently : boolean ) : Promise < void > {
178277 if ( this . autoSync . value === undefined ) {
0 commit comments