Display a notification badge using Firestore

Hey there! I am currently developing a chat application using Firestore. While researching, I found plenty of information on creating badge notifications with cloud messaging, but not much without it. Can anyone help me out with this? I want to display a notification dot on an icon when a user receives an unread message. It would be great if I could also show the total number of unread messages.

Firestore Data Structure

 users
      |
      ---- chatList (subcollection)
              ---- chatFrom: user1_Id
              ---- chatWith: user2_Id
              ---- chatRoomId: smallerUserID_biggerUserID
chatRooms
      |
      ---- smallerUserID_biggerUserID (subcollection)
              ---- content: "Hello"
              ---- id: 1613422354427
              ---- idFrom: user1_Id
              ---- timestamp: 1613422354427
                    

Fetching and Sending Messages in chatRooms Collection

getMessages() {
  this.listMessage = []; 
  
  db.collection('chatRooms').doc(this.chatRoomId).collection(this.chatRoomId)
    .onSnapshot((snapshot) => {
      snapshot.docChanges().forEach((change) => {
        if (change.type === 'added') {
          this.listMessage.push(change.doc.data());
        }
     });
  });
},

async sendMessage(content) {
  if (content.trim() === '') { return }
  
  const timestamp = moment().valueOf().toString();
  const idFrom = this.authUser.userId;
  const idTo = this.currentPeerUser.userId;
  const message = { id: timestamp, idFrom, idTo, timestamp, content };

  const chatRoomRef = db.collection('chatRooms').doc(this.chatRoomId)
                        .collection(this.chatRoomId).doc(timestamp);
  await chatRoomRef.set(message);

  this.inputValue = '';
},

Answer №1

After considering @John's suggestion, a more effective approach would involve adding an additional field to your objects indicating whether the message has been read or not. You can implement this through some simple adjustments to your getMessages() function like so:

getMessages() {
  this.listMessage = []; 
  
  db.collection('chatRooms').doc(this.chatRoomId).collection(this.chatRoomId)
    .onSnapshot((snapshot) => {
      snapshot.docChanges().forEach((change) => {
        if (change.type === 'added') {
          this.listMessage.push({
              isNew: true,
              message: change.doc.data()
          });
        }
     });
  });
}

The use of the isNew field allows you to display a new message icon based on its value. To update this value once the message is read, you can utilize the Intersection Observer by following these steps:

//options for the observer
let options = {
  root: document.querySelector('#YOUR_ROOT_ELEMENT_HERE'),
  rootMargin: '0px',
  threshold: 1.0
}

let observer = new IntersectionObserver(callback, options);

let target = document.querySelector('#YOUR_TARGET_ELEMENT_HERE');
observer.observe(target);

let callback = (entries, observer) => {
    this.listMessage.forEach(function(messageItem) {
        messageItem.isNew = false;
    });
};

In this scenario, replace YOUR_ROOT_ELEMENT_HERE with the parent element and YOUR_TARGET_ELEMENT_HERE with the unread message. When the intersection occurs, all messages will be marked as read, but feel free to customize this logic according to your requirements.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the correct way to chain Promises for the tasks of creating a new user and hashing their password simultaneously?

My goal is to send a POST request to create a new account. The process involves checking if an account with the same email exists. If not, a new account is created and stored in a user collection. Additionally, password hashing and token generation are per ...

Tips for invoking a function to automatically load elements on a jQuery mobile website

I am looking to keep my navbar HTML markup in a centralized location for easy editing. Currently, the body content of my index.html file appears like this: <div data-role="page" id="SomePage"> <div data-role="header"> <h1>Thi ...

substitute a component with a different one if it is present

I'm currently working on a script that will automatically replace one element with another as soon as it is created, even when the page loads. Despite my attempts to use MutationObserver, I haven't been successful. var target = document.querySe ...

Require assistance with a hidden file upload element using Webdriver and JavaScript

I am currently facing a challenge with automating a file upload process in a client web application. The code snippet for the file upload form is provided below: <td valign="top"> <iframe id="batchLoad:inputFile:uploadFrame" class="iceInpFile ...

Is there a way to use jQuery to enable multiple checkboxes without assigning individual IDs to each one?

I need help finding a way to efficiently select multiple checkboxes using jQuery without relying on individual ids. All of my checkboxes are organized in a consistent grouping, making it easier for me to target them collectively. To illustrate my issue, I ...

How can I filter out strings and only keep the numbers in an array using JavaScript?

After searching through numerous similar questions, I have been unable to find a solution to this particular challenge. I am in need of a function that can remove all non-number items from an array while also modifying the existing array rather than creati ...

The Render function in ReactJS is not getting refreshed

My goal is to implement a chat feature using material UI. I have set up a function where users can submit new chat messages, which then go through the reducer and are stored in the redux store. The process seems to be working fine, except for the fact that ...

Utilizing React to create an infinite loop, where an onClick event triggers an image change that updates the source of

I'm experiencing an infinite loop in my React application. I'm attempting to include a previous Image and next Image button in the development stage using an image tag. However, when my component loads up, I encounter errors. Does anyone have a ...

How can you ensure the dynamic search parameter is accurately configured within the URL?

**Is the dynamic search parameter correctly set in the URL? Should I be using backticks or other syntax? I want to search for objects within a backend URL endpoint based on user input. https://codesandbox.io/s/onscroll-izfjoc?file=/index.html** people: ...

An error in Typescript is indicating that a semicolon is expected. The identifier 'EventNameString' is currently being used as a value, even though it only refers to a type

I've been working on integrating Firebase phone authentication into an older Ionic project and have followed several tutorials. I was able to successfully implement it, but whenever I run ionic serve -l, I encounter the following error: Interestingly ...

What is the reason behind the absence of unwrapping when utilizing a ref as an element within a reactive array or reactive Map?

The Vue documentation states the following: Unlike reactive objects, there is no unwrapping performed when the ref is accessed as an element of a reactive array or a native collection type like Map Here are some examples provided in the documentation: c ...

- Utilize bullet points to exhibit keywords within a D3.js div by appending them

I'm looking to showcase the comma-separated values from a JSON file as a list in a mouseover tooltip on each node. Here is my current code snippet: div.append("div") .attr("class", "tooltip") .style("opacity", 1) .html("Node name : " + d.NodeName + ...

What is the best way to position a tooltip near an element for optimal visibility?

One div is located on the page as follows: <div id="tip"> Text for tip goes here... </div> And another one can be found below: <div class="element"> Text for element goes here... </div> There is also a piece of JavaScript ...

Find keys in an array based on a specified value

I need to retrieve an array of keys from an object that match a specified value ...

The grpc client is not able to receive the data being streamed from the

I've been attempting to stream the output of a nodejs child process through grpc, but I consistently receive an empty result. Here is my proto file: syntax = "proto3"; package servicePackage; service Mobile { rpc sign(Empty) returns ( ...

Node.js JSON parser encountering an unexpected undefined error

Whenever I attempt to JSON.parse the string, an error occurs and I receive this message: undefined:1 {"device":"FaclonTest","data":[{"tag":"LATITUDE","value":1903.5091},{"tag":"LONGITUDE","value":07251.0348}]} I'm unsure of where the mistake is. Can ...

Dynamically defined type literals in Typescript

Recently, I came across an intriguing problem. I am attempting to develop a Vue.js wizard component that takes in configuration objects. The type of demo I have looks like this: type WizardConfiguration = { steps: Array<{ name: string, fie ...

jQuery problem: Unable to access a property that is undefined

After testing my code on JSfiddle, I noticed that it works perfectly. However, when I try to implement it on a webpage with jQuery already loaded in the DOM, I encounter a console error, shown in the screenshot. I am certain that the iframe selector I am ...

What is the reason for the directive being available in $rootScope?

Currently, there doesn't seem to be a major issue but it has sparked my curiosity. I have a straightforward directive that, for some unknown reason, is accessible within $rootScope. JAVASCRIPT: (function(){ var app = angular.module('myAp ...

Using an if statement to run a script in Npm

Is there a way to configure an npm run script to use different AWS accounts based on the environment? { "config": { "acc": if ({npm_config_env} == "dev") "account1" else "account_2" }, "scr ...