Skip to content

Commit ea6d881

Browse files
feat: implement real-time notification badge using socket.io-client
1 parent 1679412 commit ea6d881

2 files changed

Lines changed: 66 additions & 7 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"react-hot-toast": "^2.5.2",
5353
"react-icons": "^5.5.0",
5454
"recharts": "2.15.4",
55+
"socket.io-client": "^4.8.3",
5556
"tailwind-merge": "^3.0.1",
5657
"tailwindcss-animate": "^1.0.7",
5758
"use-image": "^1.1.1",

src/components/layout/dashboard/navbar/notification.tsx

Lines changed: 65 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,72 @@
1+
"use client";
2+
3+
import { useEffect, useState } from "react";
14
import { Button } from "@/components/ui/button";
25
import { myFetch } from "@/utils/myFetch";
36
import { Bell } from "lucide-react";
47
import Link from "next/link";
8+
import { io } from "socket.io-client";
9+
10+
const Notification = () => {
11+
const [unreadCount, setUnreadCount] = useState(0);
12+
const [authId, setAuthId] = useState<string | null>(null);
13+
14+
const fetchUnreadCount = async () => {
15+
try {
16+
const unreadRes = await myFetch(`/notification/unread-count`);
17+
if (unreadRes?.success) {
18+
setUnreadCount(unreadRes?.data?.unreadCount ?? 0);
19+
}
20+
} catch (error) {
21+
console.error("Failed to fetch unread count", error);
22+
}
23+
};
24+
25+
useEffect(() => {
26+
// Fetch profile for authId
27+
const fetchProfile = async () => {
28+
try {
29+
const res = await myFetch("/user/profile");
30+
if (res?.success && res?.data?._id) {
31+
setAuthId(res.data._id);
32+
}
33+
} catch (error) {
34+
console.error("Failed to fetch profile for socket listener", error);
35+
}
36+
};
37+
38+
fetchProfile();
39+
fetchUnreadCount();
40+
}, []);
41+
42+
useEffect(() => {
43+
if (!authId) return;
44+
45+
// Establish WebSocket connection
46+
const serverUrl = process.env.NEXT_PUBLIC_SERVER_URL || "http://10.10.7.94:5005";
47+
const socket = io(serverUrl, {
48+
transports: ["websocket"],
49+
});
50+
51+
socket.on("connect", () => {
52+
console.log("Socket successfully connected to:", serverUrl);
53+
});
54+
55+
socket.on("connect_error", (err) => {
56+
console.error("Socket connection error:", err);
57+
});
58+
59+
const eventName = `notification::${authId}`;
60+
socket.on(eventName, () => {
61+
console.log("Real-time notification received via:", eventName);
62+
fetchUnreadCount();
63+
});
564

6-
const Notification = async () => {
7-
const unreadRes = await myFetch(`/notification/unread-count`, {
8-
tags: ["notifications"],
9-
cache: "no-store",
10-
});
11-
const unreadCount = unreadRes?.data ?? 0;
65+
return () => {
66+
socket.off(eventName);
67+
socket.disconnect();
68+
};
69+
}, [authId]);
1270

1371
return (
1472
<Link href={"/notifications"}>
@@ -19,7 +77,7 @@ const Notification = async () => {
1977
>
2078
<Bell className="size-6" />
2179
{unreadCount > 0 && (
22-
<span className="absolute -top-1.5 left-5 px-2 text-[10px] text-red-100 bg-red-600 rounded-full">
80+
<span className="absolute -top-0.5 left-4 flex items-center justify-center min-w-[18px] h-[18px] px-1 text-[9px] font-bold text-white bg-red-600 rounded-full">
2381
{unreadCount}
2482
</span>
2583
)}

0 commit comments

Comments
 (0)