@@ -24,6 +24,8 @@ import {
2424 stringify
2525} from '@firebase/util' ;
2626
27+ import { ValueEventRegistration } from '../api/Reference_impl' ;
28+
2729import { AppCheckTokenProvider } from './AppCheckTokenProvider' ;
2830import { AuthTokenProvider } from './AuthTokenProvider' ;
2931import { PersistentConnection } from './PersistentConnection' ;
@@ -61,7 +63,7 @@ import {
6163 syncTreeCalcCompleteEventCache ,
6264 syncTreeGetServerValue ,
6365 syncTreeRemoveEventRegistration ,
64- syncTreeRegisterQuery
66+ syncTreeTagForQuery
6567} from './SyncTree' ;
6668import { Indexable } from './util/misc' ;
6769import {
@@ -452,14 +454,18 @@ function repoGetNextWriteId(repo: Repo): number {
452454 * belonging to active listeners. If they are found, such values
453455 * are considered to be the most up-to-date.
454456 *
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.
459461 *
460462 * @param query - The query to surface a value for.
461463 */
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 > {
463469 // Only active queries are cached. There is no persisted cache.
464470 const cached = syncTreeGetServerValue ( repo . serverSyncTree_ , query ) ;
465471 if ( cached != null ) {
@@ -470,32 +476,57 @@ export function repoGetValue(repo: Repo, query: QueryContext): Promise<Node> {
470476 const node = nodeFromJSON ( payload ) . withIndex (
471477 query . _queryParams . getIndex ( )
472478 ) ;
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 [ ] ;
474493 if ( query . _queryParams . loadsAllData ( ) ) {
475- syncTreeApplyServerOverwrite ( repo . serverSyncTree_ , query . _path , node ) ;
494+ events = syncTreeApplyServerOverwrite (
495+ repo . serverSyncTree_ ,
496+ query . _path ,
497+ node
498+ ) ;
476499 } 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 (
483502 repo . serverSyncTree_ ,
484503 query . _path ,
485504 node ,
486505 tag
487506 ) ;
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.
490507 }
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 (
492524 repo . serverSyncTree_ ,
493525 query ,
494- null
526+ eventRegistration ,
527+ null ,
528+ true
495529 ) ;
496- if ( cancels . length > 0 ) {
497- repoLog ( repo , 'unexpected cancel events in repoGetValue' ) ;
498- }
499530 return node ;
500531 } ,
501532 err => {
0 commit comments