onSnapshot() Firestore sending changes multiple times with Flask-Sockets

596 Views Asked by At

I'm trying to develop a web chat with Flask and Firestore. I set a flow to receive new messages from firestore (when something changes at the database) and send through websockets to UI. Something like that:

Python:

@sockets.route('/messages')
def chat_socket(ws):
  message = None
  def callback_snapshot(col_snapshot, changes, read_time):
    with app.app_context():
      Messages = []
      for change in changes:
        if change.type.name == 'ADDED':
          Messages.append(change.document)
      conversation = render_template(
          'conversation.html',
          Messages = Messages,
        )
      numberID = None
      if len(col_snapshot) > 0:
        for i in col_snapshot:
          a = i 
        numberID = a.reference.parent.parent.id

      response = json.dumps({
        'conversation': conversation,
        'numberID': numberID
        })
      ws.send(response)

  while not ws.closed:

    response = json.loads(ws.receive())
    newNumberID = response['newNumberID'].strip()

    query_snapshot = fn.GetMessages(newNumberID)
    doc_watch = query_snapshot.on_snapshot(callback_snapshot)

    if message is None:
      continue

Javascript:

    function messages(numberID) {
  var scheme = window.location.protocol == "https:" ? 'wss://' : 'ws://';
  var webSocketUri =  scheme
    + window.location.hostname
    + (location.port ? ':'+location.port: '')
    + '/messages';

/* Get elements from the page */
  var form = $('#chat-form');
  var textarea = $('#chat-text');
  var output = $('.messages');
  var status = $('.messages');

  var websocket = new WebSocket(webSocketUri);

  websocket.onopen = function() {};

  websocket.onclose = function() {};

  websocket.onmessage = function(e) {    
    numberID = JSON.parse(e.data).numberID
    conversation = JSON.parse(e.data).conversation
    output.append(conversation);   
    if (numberID == null){
      output.empty();
    }};

  websocket.onerror = function(e) {console.log(e);};
  websocket.onopen = () => websocket.send(numberID);

};

The problem is: When I use col_snapshot as Messages, everything is ok besides I get the whole firestore Collection sent every time to the user when a message is sent. So it's totally not efficient. When I set callback only for changes, as described above, if I trigger the function more than one time, somehow I set multiple listeners for the same collection, so I get multiple "changes updates" in UI. How I can keep track of those listeners so I only set one listener per Collection?

1

There are 1 best solutions below

2
On

As you can see from the documentation, you should only call GetMessages and on_snapshot once per document.

@sockets.route('/messages')
def chat_socket(ws):
  message = None
  def callback_snapshot(col_snapshot, changes, read_time):
    with app.app_context():
      # Rest of the function ...
      ws.send(response)

  response = json.loads(ws.receive())
  numberID = response['newNumberID'].strip()
  query_snapshot = fn.GetMessages(numberID)
  doc_watch = query_snapshot.on_snapshot(callback_snapshot)
  while not ws.closed:
    newNumberID = response['newNumberID'].strip()
    response = json.loads(ws.receive())
    if newNumberID != numberID:
      numberID = newNumberID
      query_snapshot = fn.GetMessages(numberID)
      doc_watch = query_snapshot.on_snapshot(callback_snapshot)