@@ -36,6 +36,9 @@ const CLIENT_STATE_KEY_PREFIX = 'fs_clients';
36
36
// The format of the LocalStorage key that stores the mutation state is:
37
37
// fs_mutations_<persistence_prefix>_<batch_id> (for unauthenticated users)
38
38
// or: fs_mutations_<persistence_prefix>_<batch_id>_<user_uid>
39
+ //
40
+ // 'user_uid' is last to avoid needing to escape '_' characters that it might
41
+ // contain.
39
42
const MUTATION_BATCH_KEY_PREFIX = 'fs_mutations' ;
40
43
41
44
/**
@@ -53,11 +56,11 @@ export type ClientKey = string;
53
56
* `SharedClientState` is primarily used for synchronization in Multi-Tab
54
57
* environments. Each tab is responsible for registering its active query
55
58
* targets and mutations. `SharedClientState` will then notify the listener
56
- * passed to `subscribe() ` for updates to mutations and queries that originated
57
- * in other clients.
59
+ * assigned to `.syncEngine ` for updates to mutations and queries that
60
+ * originated in other clients.
58
61
*
59
- * To receive notifications, both `subscribe()` and `start()` have to be called
60
- * in order .
62
+ * To receive notifications, `.syncEngine` has to be assigned before calling
63
+ * `start()` .
61
64
*
62
65
* TODO(multitab): Add callbacks to SyncEngine
63
66
*/
@@ -115,7 +118,7 @@ interface MutationMetadataSchema {
115
118
}
116
119
117
120
/**
118
- * Holds the state of a mutation batch, including its user ID, batch ID annd
121
+ * Holds the state of a mutation batch, including its user ID, batch ID and
119
122
* whether the batch is 'pending', 'acknowledged' or 'rejected'.
120
123
*/
121
124
// Visible for testing
@@ -181,15 +184,18 @@ export class MutationMetadata {
181
184
}
182
185
183
186
toLocalStorageJSON ( ) : string {
184
- const batchState : MutationMetadataSchema = {
187
+ const batchMetadata : MutationMetadataSchema = {
185
188
state : this . state
186
189
} ;
187
190
188
191
if ( this . error ) {
189
- batchState . error = { code : this . error . code , message : this . error . message } ;
192
+ batchMetadata . error = {
193
+ code : this . error . code ,
194
+ message : this . error . message
195
+ } ;
190
196
}
191
197
192
- return JSON . stringify ( batchState ) ;
198
+ return JSON . stringify ( batchMetadata ) ;
193
199
}
194
200
}
195
201
@@ -366,13 +372,14 @@ export class LocalClientState implements ClientState {
366
372
*/
367
373
// TODO(multitab): Rename all usages of LocalStorage to WebStorage to better differentiate from LocalClient.
368
374
export class WebStorageSharedClientState implements SharedClientState {
375
+ syncEngine : SharedClientStateSyncer | null ;
376
+
369
377
private readonly storage : Storage ;
370
378
private readonly localClientStorageKey : string ;
371
379
private readonly activeClients : { [ key : string ] : ClientState } = { } ;
372
380
private readonly storageListener = this . handleLocalStorageEvent . bind ( this ) ;
373
381
private readonly clientStateKeyRe : RegExp ;
374
382
private readonly mutationBatchKeyRe : RegExp ;
375
- private syncEngine : SharedClientStateSyncer | null ;
376
383
private user : User ;
377
384
private started = false ;
378
385
@@ -395,7 +402,7 @@ export class WebStorageSharedClientState implements SharedClientState {
395
402
`^${ CLIENT_STATE_KEY_PREFIX } _${ persistenceKey } _([^_]*)$`
396
403
) ;
397
404
this . mutationBatchKeyRe = new RegExp (
398
- `^${ MUTATION_BATCH_KEY_PREFIX } _${ persistenceKey } _(\\d* )(?:_(.*))$`
405
+ `^${ MUTATION_BATCH_KEY_PREFIX } _${ persistenceKey } _(\\d+ )(?:_(.*))? $`
399
406
) ;
400
407
}
401
408
@@ -404,17 +411,9 @@ export class WebStorageSharedClientState implements SharedClientState {
404
411
return typeof window !== 'undefined' && window . localStorage != null ;
405
412
}
406
413
407
- subscribe ( syncEngine : SharedClientStateSyncer ) {
408
- this . syncEngine = syncEngine ;
409
- }
410
-
411
414
// TODO(multitab): Handle user changes.
412
415
start ( initialUser : User , knownClients : ClientKey [ ] ) : void {
413
416
assert ( ! this . started , 'WebStorageSharedClientState already started' ) ;
414
- assert (
415
- this . syncEngine !== null ,
416
- 'Start() called before subscribing to events'
417
- ) ;
418
417
window . addEventListener ( 'storage' , this . storageListener ) ;
419
418
420
419
for ( const clientKey of knownClients ) {
@@ -497,21 +496,21 @@ export class WebStorageSharedClientState implements SharedClientState {
497
496
'Received LocalStorage notification for local change.'
498
497
) ;
499
498
500
- if ( event . newValue == null ) {
501
- if ( this . clientStateKeyRe . test ( event . key ) ) {
502
- const clientId = this . fromLocalStorageClientStateKey ( event . key ) ;
503
- delete this . activeClients [ clientId ] ;
504
- }
505
- } else {
506
- if ( this . clientStateKeyRe . test ( event . key ) ) {
499
+ if ( this . clientStateKeyRe . test ( event . key ) ) {
500
+ if ( event . newValue != null ) {
507
501
const clientState = this . fromLocalStorageClientState (
508
502
event . key ,
509
503
event . newValue
510
504
) ;
511
505
if ( clientState ) {
512
506
this . activeClients [ clientState . clientId ] = clientState ;
513
507
}
514
- } else if ( this . mutationBatchKeyRe . test ( event . key ) ) {
508
+ } else {
509
+ const clientId = this . fromLocalStorageClientStateKey ( event . key ) ;
510
+ delete this . activeClients [ clientId ] ;
511
+ }
512
+ } else if ( this . mutationBatchKeyRe . test ( event . key ) ) {
513
+ if ( event . newValue !== null ) {
515
514
const mutationMetadata = this . fromLocalStorageMutationMetadata (
516
515
event . key ,
517
516
event . newValue
@@ -618,6 +617,11 @@ export class WebStorageSharedClientState implements SharedClientState {
618
617
private async handleMutationBatchEvent (
619
618
mutationBatch : MutationMetadata
620
619
) : Promise < void > {
620
+ assert (
621
+ this . syncEngine !== null ,
622
+ 'syncEngine property must be set in order to handle events'
623
+ ) ;
624
+
621
625
if ( mutationBatch . user . uid !== this . user . uid ) {
622
626
debug (
623
627
LOG_TAG ,
0 commit comments