@@ -24,6 +24,8 @@ import {
24
24
stringify
25
25
} from '@firebase/util' ;
26
26
27
+ import { ValueEventRegistration } from '../api/Reference_impl' ;
28
+
27
29
import { AppCheckTokenProvider } from './AppCheckTokenProvider' ;
28
30
import { AuthTokenProvider } from './AuthTokenProvider' ;
29
31
import { PersistentConnection } from './PersistentConnection' ;
@@ -61,7 +63,7 @@ import {
61
63
syncTreeCalcCompleteEventCache ,
62
64
syncTreeGetServerValue ,
63
65
syncTreeRemoveEventRegistration ,
64
- syncTreeRegisterQuery
66
+ syncTreeTagForQuery
65
67
} from './SyncTree' ;
66
68
import { Indexable } from './util/misc' ;
67
69
import {
@@ -452,14 +454,18 @@ function repoGetNextWriteId(repo: Repo): number {
452
454
* belonging to active listeners. If they are found, such values
453
455
* are considered to be the most up-to-date.
454
456
*
455
- * If the client is not connected, this method will try to
456
- * establish a connection and request the value for `query`. If
457
- * the client is not able to retrieve the query result, it reports
458
- * an error.
457
+ * If the client is not connected, this method will wait until the
458
+ * repo has established a connection and then request the value for `query`.
459
+ * If the client is not able to retrieve the query result for another reason,
460
+ * it reports an error.
459
461
*
460
462
* @param query - The query to surface a value for.
461
463
*/
462
- export function repoGetValue ( repo : Repo , query : QueryContext ) : Promise < Node > {
464
+ export function repoGetValue (
465
+ repo : Repo ,
466
+ query : QueryContext ,
467
+ eventRegistration : ValueEventRegistration
468
+ ) : Promise < Node > {
463
469
// Only active queries are cached. There is no persisted cache.
464
470
const cached = syncTreeGetServerValue ( repo . serverSyncTree_ , query ) ;
465
471
if ( cached != null ) {
@@ -470,32 +476,57 @@ export function repoGetValue(repo: Repo, query: QueryContext): Promise<Node> {
470
476
const node = nodeFromJSON ( payload ) . withIndex (
471
477
query . _queryParams . getIndex ( )
472
478
) ;
473
- // if this is a filtered query, then overwrite at path
479
+ /**
480
+ * Below we simulate the actions of an `onlyOnce` `onValue()` event where:
481
+ * Add an event registration,
482
+ * Update data at the path,
483
+ * Raise any events,
484
+ * Cleanup the SyncTree
485
+ */
486
+ syncTreeAddEventRegistration (
487
+ repo . serverSyncTree_ ,
488
+ query ,
489
+ eventRegistration ,
490
+ true
491
+ ) ;
492
+ let events : Event [ ] ;
474
493
if ( query . _queryParams . loadsAllData ( ) ) {
475
- syncTreeApplyServerOverwrite ( repo . serverSyncTree_ , query . _path , node ) ;
494
+ events = syncTreeApplyServerOverwrite (
495
+ repo . serverSyncTree_ ,
496
+ query . _path ,
497
+ node
498
+ ) ;
476
499
} else {
477
- // Simulate `syncTreeAddEventRegistration` without events/listener setup.
478
- // We do this (along with the syncTreeRemoveEventRegistration` below) so that
479
- // `repoGetValue` results have the same cache effects as initial listener(s)
480
- // updates.
481
- const tag = syncTreeRegisterQuery ( repo . serverSyncTree_ , query ) ;
482
- syncTreeApplyTaggedQueryOverwrite (
500
+ const tag = syncTreeTagForQuery ( repo . serverSyncTree_ , query ) ;
501
+ events = syncTreeApplyTaggedQueryOverwrite (
483
502
repo . serverSyncTree_ ,
484
503
query . _path ,
485
504
node ,
486
505
tag
487
506
) ;
488
- // Call `syncTreeRemoveEventRegistration` with a null event registration, since there is none.
489
- // Note: The below code essentially unregisters the query and cleans up any views/syncpoints temporarily created above.
490
507
}
491
- const cancels = syncTreeRemoveEventRegistration (
508
+ /*
509
+ * We need to raise events in the scenario where `get()` is called at a parent path, and
510
+ * while the `get()` is pending, `onValue` is called at a child location. While get() is waiting
511
+ * for the data, `onValue` will register a new event. Then, get() will come back, and update the syncTree
512
+ * and its corresponding serverCache, including the child location where `onValue` is called. Then,
513
+ * `onValue` will receive the event from the server, but look at the syncTree and see that the data received
514
+ * from the server is already at the SyncPoint, and so the `onValue` callback will never get fired.
515
+ * Calling `eventQueueRaiseEventsForChangedPath()` is the correct way to propagate the events and
516
+ * ensure the corresponding child events will get fired.
517
+ */
518
+ eventQueueRaiseEventsForChangedPath (
519
+ repo . eventQueue_ ,
520
+ query . _path ,
521
+ events
522
+ ) ;
523
+ syncTreeRemoveEventRegistration (
492
524
repo . serverSyncTree_ ,
493
525
query ,
494
- null
526
+ eventRegistration ,
527
+ null ,
528
+ true
495
529
) ;
496
- if ( cancels . length > 0 ) {
497
- repoLog ( repo , 'unexpected cancel events in repoGetValue' ) ;
498
- }
499
530
return node ;
500
531
} ,
501
532
err => {
0 commit comments