5

Simple version: When I register to onSnapshot in Firestore, is there a way to know if I am the client that pushed the update?

Detailed Version:
When a webpage is opened, it registers an onSnapshot callback to a Firestore collection called notes. The collection is then downloaded and stored in a local array var notes = [].

var window.notes = []
let notesRef = refs.db.collection('notes')
notesRef.onSnapshot(querySnapshot => {
  querySnapshot.docChanges.forEach((docChange) => {
    switch (docChange.type) {
      case 'added':
        notes.push(docChange.doc.data())
        break
    }
  })
}

We then update a note locally and push it to the database.

notes[0] =  'changed contents'
firestore.notesRef.doc(0).set(notes[0])

This will send the note out to firestore, update it in the remote database, then because we're registered to onSnapshot for the notes collection we'll trigger that event and get a modified change.

let notesRef = refs.db.collection('notes')
notesRef.onSnapshot(querySnapshot => {
  querySnapshot.docChanges.forEach((docChange) => {
    switch (docChange.type) {
      case 'added':
        notes.push(docChange.doc.data())
        break
      case 'modified':
        // ********** We end up here!!! ***********
        // Probably want to do:
        notes[docChange.doc.id] = docChange.doc.data()
        break
    }
  })
}

Is there a way to detect if this same client called the set that triggered the modified docChange?
The point is this: If the user made the change themself then they don't care about the change ad we should ignore it. If a different user made the change then this user should be given a choice of whether or not they want to update their local version.

In my experience, when firestore.notesRef.doc(0).set(notes[0]) is called it takes some time to receive the update from the server. If the local user modifies the note again in this time but hasn't pushed it to the server yet then their changes are lost (overridden by the servers version - the version they last sent to the server). It's essentially a race condition.

So, when I register to onSnapshot in Firestore, is there a way to know if I am the client that pushed the update?

MattCochrane
  • 2,900
  • 2
  • 25
  • 35

1 Answers1

6

You can see if it was a local or remote change with:

docChange.doc.metadata.hasPendingWrites

If hasPendingWrites is true, then origin of the update was local. The local cache has been updated and the write to the server is still pending. If hasPendingWrites is false, then the origin of the update was remote.

See how it can be used with onSnapshot in this snippet:

let notesRef = refs.db.collection('notes')
notesRef.onSnapshot(querySnapshot => {
  querySnapshot.docChanges.forEach((docChange) => {
    switch (docChange.type) {
      case 'added':
        notes.push(docChange.doc.data())
        break
      case 'modified':
        // ********** New Code ***********
        let source = docChange.doc.metadata.hasPendingWrites ? 'Local' : 'Server'
        if (source === 'Server') {
          notes[docChange.doc.id] = docChange.doc.data()
        } else {
          // Do nothing, it's a local update so ignore it
        }
        // ********** New Code End ***********
        break
    }
  })
}
MattCochrane
  • 2,900
  • 2
  • 25
  • 35
  • 2
    Is there any way to know if the a document update from the server was due to a client initiated _transaction_ ? – Ian Aug 26 '20 at 21:32