» Make Web Chat App Front-End with React » 2. Development » 2.7 Notifications

Notifications

One-on-One Chat

Add a red dot in src/components/contact.jsx:

@@ -4,6 +4,7 @@ import "./Contact.css";
 const Contact = (props) => {
   return (
     <div className="contact" onClick={props.onClick}>
+      {props.notify && <div className="notify-dot"></div>}
       <div className="name truncate">{props.username}</div>
       <div className="last-message truncate">
         {props.message || "[no messages]"}

Add red dot's styles in src/components/Contact.css:

@@ -5,6 +5,7 @@
   border-bottom: solid 1px var(--theme-color);
   text-align: left;
   cursor: pointer;
+  position: relative;
 }
 
 .name {
@@ -28,3 +29,13 @@
   overflow: hidden;
   text-overflow: ellipsis;
 }
+
+.notify-dot {
+  position: absolute;
+  top: 8px;
+  right: 8px;
+  width: 8px;
+  height: 8px;
+  background-color: orangered;
+  border-radius: 50%;
+}

Add a new state contactNotifications to remember the contacts that need red dots.

Changes in src/App.js:

@@ -31,6 +31,7 @@ function App() {
   const [groups, setGroups] = useState([]);
   const [contactMessages, setContactMessages] = useState({});
   const [groupMessages, setGroupMessages] = useState({});
+  const [contactNotifications, setContactNotifications] = useState({});
   const [pickedContact, setPickedContact] = useState(null);
   const [typedContent, setTypedContent] = useState("");
   const resultEndRef = useRef(null);
@@ -53,6 +54,9 @@ function App() {
         const newMessages = [...oldMessages, entry];
         return { ...cm, [from]: newMessages };
       });
+      setContactNotifications((cn) => {
+        return { ...cn, [from]: true };
+      });
     });
     socket.on("create-group", (data) => {
       const { name, id } = data;
@@ -145,6 +149,11 @@ function App() {
     }
     return "";
   };
+  const readMessage = (id) => {
+    setContactNotifications((cn) => {
+      return { ...cn, [id]: false };
+    });
+  };
   return (
     <>
       <div className="app">
@@ -197,8 +206,16 @@ function App() {
                         key={e.sid}
                         username={e.emoji + " " + e.name}
                         message={lastMessage(contactMessages[e.sid])}
+                        notify={
+                          e.sid !== pickedContact?.sid &&
+                          contactNotifications[e.sid]
+                        }
                         onClick={() => {
                           setPickedContact(e);
+                          if (pickedContact) {
+                            readMessage(pickedContact.sid);
+                          }
+                          readMessage(e.sid);
                         }}
                       />
                     ))}

Group Chat

Add a red dot in src/components/group.jsx:

@@ -4,6 +4,7 @@ import "./Contact.css";
 const Group = (props) => {
   return (
     <div className="contact" onClick={props.onClick}>
+      {props.notify && <div className="notify-dot"></div>}
       <div className="name truncate">{props.name}</div>
       <div className="last-message truncate">
         {props.message || "[no messages]"}
  • Add a new state groupNotifications to remember the groups that need red dots.
  • Add red dots for tabs: Chat and Groups. When you're in one tab, the other tab would show a red dot if it gets any new message.

Changes in src/App.js:

@@ -32,6 +32,7 @@ function App() {
   const [contactMessages, setContactMessages] = useState({});
   const [groupMessages, setGroupMessages] = useState({});
   const [contactNotifications, setContactNotifications] = useState({});
+  const [groupNotifications, setGroupNotifications] = useState({});
   const [pickedContact, setPickedContact] = useState(null);
   const [typedContent, setTypedContent] = useState("");
   const resultEndRef = useRef(null);
@@ -73,6 +74,9 @@ function App() {
         const newMessages = [...oldMessages, entry];
         return { ...gm, [roomId]: newMessages };
       });
+      setGroupNotifications((gn) => {
+        return { ...gn, [roomId]: true };
+      });
     });
     socket.emit("user-join", user);
     setConn(socket);
@@ -150,9 +154,24 @@ function App() {
     return "";
   };
   const readMessage = (id) => {
-    setContactNotifications((cn) => {
-      return { ...cn, [id]: false };
-    });
+    if (isGroupChatting) {
+      setGroupNotifications((gn) => {
+        return { ...gn, [id]: false };
+      });
+    } else {
+      setContactNotifications((cn) => {
+        return { ...cn, [id]: false };
+      });
+    }
+  };
+  const shouldNotify = () => {
+    if (isGroupChatting) {
+      // Notify for Chat Tab
+      return Object.values(contactNotifications).some((e) => e === true);
+    } else {
+      // Notify for Groups Tab
+      return Object.values(groupNotifications).some((e) => e === true);
+    }
   };
   return (
     <>
@@ -174,6 +193,9 @@ function App() {
                   setPickedContact(null);
                 }}
               >
+                {isGroupChatting && shouldNotify() && (
+                  <span className="notify-dot"></span>
+                )}
                 Chat
               </span>
               <span
@@ -185,6 +207,9 @@ function App() {
                   setPickedContact(null);
                 }}
               >
+                {!isGroupChatting && shouldNotify() && (
+                  <span className="notify-dot"></span>
+                )}
                 Groups
               </span>
             </div>
@@ -196,8 +221,15 @@ function App() {
                         key={e.id}
                         name={e.name}
                         message={lastMessage(groupMessages[e.id])}
+                        notify={
+                          e.id !== pickedContact?.id && groupNotifications[e.id]
+                        }
                         onClick={() => {
                           setPickedContact(e);
+                          if (pickedContact) {
+                            readMessage(pickedContact.id);
+                          }
+                          readMessage(e.id);
                         }}
                       />
                     ))

Tune styles in src/App.css:

@@ -28,6 +28,12 @@ body {
   background-color: var(--secondary-color);
   padding: 5px 12px;
   cursor: pointer;
+  position: relative;
+
+  .notify-dot {
+    top: 4px;
+    right: 4px;
+  }
 }
 
 .left-seg {

Avoid red dots for the current conversation

Changes in src/app.js:

@@ -92,6 +92,20 @@ function App() {
       resultEndRef.current?.scrollIntoView({ behavior: "smooth" });
   }, [contactMessages, groupMessages, pickedContact]);
 
+  useEffect(() => {
+    // Avoid red dots for the current conversation
+    if (!pickedContact) return;
+    if (isGroupChatting) {
+      setGroupNotifications((gn) => {
+        return { ...gn, [pickedContact.id]: false };
+      });
+    } else {
+      setContactNotifications((cn) => {
+        return { ...cn, [pickedContact.sid]: false };
+      });
+    }
+  }, [contactMessages, groupMessages, pickedContact, isGroupChatting]);
+
   const login = (emoji, name) => {
     setUser({ emoji, name });
     setLogged(true);
@@ -226,9 +240,6 @@ function App() {
                         }
                         onClick={() => {
                           setPickedContact(e);
-                          if (pickedContact) {
-                            readMessage(pickedContact.id);
-                          }
                           readMessage(e.id);
                         }}
                       />
@@ -244,9 +255,6 @@ function App() {
                         }
                         onClick={() => {
                           setPickedContact(e);
-                          if (pickedContact) {
-                            readMessage(pickedContact.sid);
-                          }
                           readMessage(e.sid);
                         }}
                       />

We use useEffect to watch changes in contactMessages and groupMessages, then turn off the red dots accordingly.

PrevNext