private _listenForNewGeohashes()

in packages/geofire/src/GeoQuery.ts [431:503]


  private _listenForNewGeohashes(): void {
    // Get the list of geohashes to query
    let geohashesToQuery: string[] = geohashQueryBounds(this._center, this._radius * 1000).map(this._queryToString);

    // Filter out duplicate geohashes
    geohashesToQuery = geohashesToQuery.filter((geohash: string, i: number) => geohashesToQuery.indexOf(geohash) === i);

    // For all of the geohashes that we are already currently querying, check if they are still
    // supposed to be queried. If so, don't re-query them. Otherwise, mark them to be un-queried
    // next time we clean up the current geohashes queried dictionary.
    const keys: string[] = Object.keys(this._currentGeohashesQueried);
    keys.forEach((geohashQueryStr: string) => {
      const index: number = geohashesToQuery.indexOf(geohashQueryStr);
      if (index === -1) {
        this._currentGeohashesQueried[geohashQueryStr].active = false;
      } else {
        this._currentGeohashesQueried[geohashQueryStr].active = true;
        geohashesToQuery.splice(index, 1);
      }
    });

    // If we are not already cleaning up the current geohashes queried and we have more than 25 of them,
    // kick off a timeout to clean them up so we don't create an infinite number of unneeded queries.
    if (this._geohashCleanupScheduled === false && Object.keys(this._currentGeohashesQueried).length > 25) {
      this._geohashCleanupScheduled = true;
      this._cleanUpCurrentGeohashesQueriedTimeout = setTimeout(() => {
        this._cleanUpCurrentGeohashesQueried();
      }, 10);
    }

    // Keep track of which geohashes have been processed so we know when to fire the 'ready' event
    this._outstandingGeohashReadyEvents = geohashesToQuery.slice();

    // Loop through each geohash to query for and listen for new geohashes which have the same prefix.
    // For every match, attach a value callback which will fire the appropriate events.
    // Once every geohash to query is processed, fire the 'ready' event.
    geohashesToQuery.forEach((toQueryStr: string) => {
      // decode the geohash query string
      const query: string[] = this._stringToQuery(toQueryStr);

      // Create the Firebase query
      const firebaseQuery: DatabaseTypes.Query = this._firebaseRef.orderByChild('g').startAt(query[0]).endAt(query[1]);

      // For every new matching geohash, determine if we should fire the 'key_entered' event
      const childAddedCallback = firebaseQuery.on('child_added', (a) => this._childAddedCallback(a));
      const childRemovedCallback = firebaseQuery.on('child_removed', (a) => this._childRemovedCallback(a));
      const childChangedCallback = firebaseQuery.on('child_changed', (a) => this._childChangedCallback(a));

      // Once the current geohash to query is processed, see if it is the last one to be processed
      // and, if so, mark the value event as fired.
      // Note that Firebase fires the 'value' event after every 'child_added' event fires.
      const valueCallback = firebaseQuery.on('value', () => {
        firebaseQuery.off('value', valueCallback);
        this._geohashQueryReadyCallback(toQueryStr);
      });

      // Add the geohash query to the current geohashes queried dictionary and save its state
      this._currentGeohashesQueried[toQueryStr] = {
        active: true,
        childAddedCallback,
        childRemovedCallback,
        childChangedCallback,
        valueCallback
      };
    });
    // Based upon the algorithm to calculate geohashes, it's possible that no 'new'
    // geohashes were queried even if the client updates the radius of the query.
    // This results in no 'READY' event being fired after the .updateCriteria() call.
    // Check to see if this is the case, and trigger the 'READY' event.
    if (geohashesToQuery.length === 0) {
      this._geohashQueryReadyCallback();
    }
  }