Skip to content
Open
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
7 changes: 4 additions & 3 deletions client/src/components/ui/NotificationDropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, { useState, useEffect, useRef } from "react";
import {
Bell,
X,
Check,
CheckCheck,
MessageSquare,
Award,
Expand All @@ -16,7 +15,7 @@ import {
markAllNotificationsAsRead,
} from "../../utils/api";
import { Notification } from "../../types/discussions";
import useSocket from "../../hooks/useSocket";
import { useSocket } from "../../contexts/SocketContext";

const NotificationDropdown: React.FC = () => {
const [notifications, setNotifications] = useState<Notification[]>([]);
Expand All @@ -37,7 +36,9 @@ const NotificationDropdown: React.FC = () => {

// Listen for real-time notifications
useEffect(() => {
if (!socket) return;
if (!socket) {
return;
}

socket.on("new_notification", (notification: Notification) => {
setNotifications((prev) => [notification, ...prev]);
Expand Down
1 change: 0 additions & 1 deletion server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

61 changes: 30 additions & 31 deletions server/routes/discussions.js
Original file line number Diff line number Diff line change
Expand Up @@ -342,29 +342,31 @@ router.post("/:id/answers", authenticateToken, async (req, res) => {

// Handle mentions
const mentions = extractMentions(content);

for (const mentionedUsername of mentions) {
const mentionedUser = await prisma.user.findUnique({
where: { username: mentionedUsername },
});

if (mentionedUser && mentionedUser.id !== req.user.id) {
await createNotification(
mentionedUser.id,
"mention",
"You were mentioned",
`${req.user.username} mentioned you in an answer`,
answer.id,
"answer",
req.user.id,
req.user.username,
req.io
);
if (mentionedUser) {
if (mentionedUser.id !== req.user.id) {
await createNotification(
mentionedUser.id,
"mention",
"You were mentioned",
`${req.user.username} mentioned you in an answer`,
answer.id,
"answer",
req.user.id,
req.user.username,
req.io
);
}
}
}

// Emit real-time event
if (req.io) {
console.log("🚀 Emitting new answer event:", id);
emitNewAnswer(req.io, id, {
...answer,
author_username: req.user.username,
Expand Down Expand Up @@ -396,7 +398,6 @@ router.post(
"/answers/:answerId/replies",
authenticateToken,
async (req, res) => {
console.log("🔥 Reply route hit! AnswerId:", req.params.answerId);
try {
const { answerId } = req.params;
const { content, images } = req.body;
Expand Down Expand Up @@ -443,33 +444,31 @@ router.post(

// Handle mentions
const mentions = extractMentions(content);

for (const mentionedUsername of mentions) {
const mentionedUser = await prisma.user.findUnique({
where: { username: mentionedUsername },
});

if (mentionedUser && mentionedUser.id !== req.user.id) {
await createNotification(
mentionedUser.id,
"mention",
"You were mentioned",
`${req.user.username} mentioned you in a reply`,
reply.id,
"reply",
req.user.id,
req.user.username,
req.io
);
if (mentionedUser) {
if (mentionedUser.id !== req.user.id) {
await createNotification(
mentionedUser.id,
"mention",
"You were mentioned",
`${req.user.username} mentioned you in a reply`,
reply.id,
"reply",
req.user.id,
req.user.username,
req.io
);
}
}
}

// Emit real-time event
if (req.io) {
console.log(
"🚀 Emitting new reply event:",
answer.discussionId,
answerId
);
emitNewReply(req.io, answer.discussionId, answerId, {
...reply,
author_username: req.user.username,
Expand Down
60 changes: 5 additions & 55 deletions server/socket/socketHandlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ const authenticateSocket = async (socket, next) => {
}

if (!token) {
console.log("❌ Socket auth: No token found in any location");
return next(new Error("Authentication error"));
}

Expand All @@ -39,11 +38,9 @@ const authenticateSocket = async (socket, next) => {
});

if (!user) {
console.log("❌ Socket auth: User not found for token");
return next(new Error("User not found"));
}

console.log("✅ Socket authenticated:", user.username);
socket.user = user;
next();
} catch (error) {
Expand All @@ -57,42 +54,19 @@ export const setupSocketHandlers = (io) => {
io.use(authenticateSocket);

io.on("connection", (socket) => {
console.log(
`✅ User ${socket.user.username} connected with socket ID: ${socket.id}`
);

// Join user to their personal room for notifications
socket.join(`user_${socket.user.id}`);
const userRoom = `user_${socket.user.id}`;
socket.join(userRoom);

// Join discussion room
socket.on("join_discussion", (discussionId) => {
const roomName = `discussion_${discussionId}`;
socket.join(roomName);
console.log(
`✅ User ${socket.user.username} joined discussion room: ${roomName}`
);

// Check room membership immediately after joining
setTimeout(() => {
const room = io.sockets.adapter.rooms.get(roomName);
const clientCount = room ? room.size : 0;
console.log(`👥 Clients in room ${roomName} after join:`, clientCount);

// List all rooms this socket is in
const socketRooms = Array.from(socket.rooms);
console.log(
`🏠 Socket ${socket.user.username} is in rooms:`,
socketRooms
);
}, 100);
});

// Leave discussion room
socket.on("leave_discussion", (discussionId) => {
socket.leave(`discussion_${discussionId}`);
console.log(
`User ${socket.user.username} left discussion ${discussionId}`
);
});

// Handle typing indicators
Expand Down Expand Up @@ -125,7 +99,6 @@ export const setupSocketHandlers = (io) => {

// Test event handler
socket.on("test_event", (data) => {
console.log(`🧪 Test event from ${socket.user.username}:`, data);
socket.emit("test_event", {
message: `Hello back from server, ${socket.user.username}!`,
timestamp: new Date().toISOString(),
Expand All @@ -134,43 +107,19 @@ export const setupSocketHandlers = (io) => {

// Handle disconnect
socket.on("disconnect", (reason) => {
console.log(
`❌ User ${socket.user.username} disconnected. Reason:`,
reason
);
console.log(
`🏠 Socket ${socket.id} was in rooms:`,
Array.from(socket.rooms)
);
// Silent disconnect
});
});
};

// Helper functions to emit events from routes
export const emitNewAnswer = (io, discussionId, answer) => {
const roomName = `discussion_${discussionId}`;
console.log("📡 Emitting new_answer to room:", roomName);

// Check how many clients are in the room
const room = io.sockets.adapter.rooms.get(roomName);
const clientCount = room ? room.size : 0;
console.log(`👥 Clients in room ${roomName}:`, clientCount);

io.to(roomName).emit("new_answer", answer);
};

export const emitNewReply = (io, discussionId, answerId, reply) => {
const roomName = `discussion_${discussionId}`;
console.log("📡 Emitting new_reply to room:", roomName, {
answerId,
reply,
});

// Check how many clients are in the room
const room = io.sockets.adapter.rooms.get(roomName);
const clientCount = room ? room.size : 0;
console.log(`👥 Clients in room ${roomName}:`, clientCount);

io.to(roomName).emit("new_reply", {
answerId,
reply,
Expand Down Expand Up @@ -198,5 +147,6 @@ export const emitVoteUpdate = (
};

export const emitNotification = (io, userId, notification) => {
io.to(`user_${userId}`).emit("new_notification", notification);
const userRoom = `user_${userId}`;
io.to(userRoom).emit("new_notification", notification);
};