Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/components/Sidebar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default function Sidebar({ onSelectConversation, selectedConversation })
const [loading, setLoading] = useState(true);
const [search, setSearch] = useState("");
const { user: currentUser } = useAuth();
const { socket } = useSocket();
const { socket, onlineUserIds } = useSocket();

useEffect(() => {
const fetchConversations = async () => {
Expand Down Expand Up @@ -98,6 +98,7 @@ export default function Sidebar({ onSelectConversation, selectedConversation })
const otherUser = conv.participants?.filter(
(p) => p._id !== currentUser._id
)[0];
const isOtherOnline = otherUser?._id && onlineUserIds?.includes(otherUser._id);

return (
<div
Expand All @@ -107,8 +108,14 @@ export default function Sidebar({ onSelectConversation, selectedConversation })
${isActive ? "bg-blue-100 font-medium" : "bg-gray-50 hover:bg-gray-100"}
`}
>
<div className="font-semibold">
<div className="font-semibold flex items-center gap-2">
{otherUser?.username || "Unknown User"}
<span
className={`inline-block w-2.5 h-2.5 rounded-full ${
isOtherOnline ? "bg-green-500" : "bg-gray-300"
}`}
title={isOtherOnline ? "Online" : "Offline"}
/>
</div>

{conv.lastMessage && (
Expand Down
44 changes: 44 additions & 0 deletions src/context/SocketContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const SocketProvider = ({ children }) => {
const socketRef = useRef(null);
const [socketReady, setSocketReady] = useState(false);
const [messages, setMessages] = useState([]);
const [onlineUserIds, setOnlineUserIds] = useState([]);

useEffect(() => {
if (!user) return;
Expand All @@ -26,6 +27,9 @@ export const SocketProvider = ({ children }) => {
socketRef.current.emit("join_conversation", convIds);
console.log("Joined conversation rooms:", convIds);
}

// Request initial online users list when connected
socketRef.current.emit("get_online_users");
});

socketRef.current.on("disconnect", (reason) => {
Expand Down Expand Up @@ -56,6 +60,45 @@ export const SocketProvider = ({ children }) => {
};
}, [socketReady]);

// Presence: track online users and live updates
useEffect(() => {
if (!socketRef.current) return;
const socket = socketRef.current;

const setInitialOnline = (ids) => {
if (Array.isArray(ids)) {
setOnlineUserIds(ids.filter(Boolean));
}
};

const handleUserConnected = (payload) => {
const id = typeof payload === "string" ? payload : payload?.userId;
if (!id) return;
setOnlineUserIds((prev) => (prev.includes(id) ? prev : [...prev, id]));
};

const handleUserDisconnected = (payload) => {
const id = typeof payload === "string" ? payload : payload?.userId;
if (!id) return;
setOnlineUserIds((prev) => prev.filter((uid) => uid !== id));
};

// Support a few common event names
socket.on("online_users", setInitialOnline);
socket.on("user_connected", handleUserConnected);
socket.on("user_disconnected", handleUserDisconnected);
socket.on("user_online", handleUserConnected);
socket.on("user_offline", handleUserDisconnected);

return () => {
socket.off("online_users", setInitialOnline);
socket.off("user_connected", handleUserConnected);
socket.off("user_disconnected", handleUserDisconnected);
socket.off("user_online", handleUserConnected);
socket.off("user_offline", handleUserDisconnected);
};
}, [socketReady]);

const sendMessage = (conversationId, text, media = null) => {
if (!socketRef.current) return;
socketRef.current.emit("send_message", { conversationId, text, media });
Expand All @@ -67,6 +110,7 @@ export const SocketProvider = ({ children }) => {
socket: socketRef.current,
messages,
sendMessage,
onlineUserIds,
}}
>
{children}
Expand Down