» React创建在线聊天软件 Web Chat App 前端 » 2. 开发 » 2.7 红点通知

红点通知

一对一聊天

添加小红点,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]"}

添加红点风格,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%;
+}

添加新的 state contactNotifications,用于记住需要红点的联系人。

变更 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);
                         }}
                       />
                     ))}

群聊天

添加红点,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]"}
  • 添加一个新 state groupNotifications,用于记住需要红点的群组。
  • ChatGroups 标签卡添加红点。当你在一个标签卡下,另一边的标签卡有新消息时会显示红点。

修改 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);
                         }}
                       />
                     ))

调整风格,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 {

避免当前对话框误生成红点

变更 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);
                         }}
                       />

我们使用 useEffect 来监测 contactMessagesgroupMessages 的状态变化,然后相应地关闭红点。