diff --git a/src/app/chat/page.tsx b/src/app/chat/page.tsx index c52546f..374035d 100644 --- a/src/app/chat/page.tsx +++ b/src/app/chat/page.tsx @@ -4,7 +4,16 @@ import { useState, useEffect, useRef, useMemo } from "react"; import { useRouter } from "next/navigation"; import { useAuth } from "@/context/AuthContext"; import { useSocket, type ChatMessage, type RoomNotice } from "@/context/SocketContext"; -import { fetchConversations, createConversation, fetchAllUsers, ConversationType, addParticipant, removeParticipant, fetchMessages } from "@/lib/api"; +import { + fetchConversations, + createConversation, + fetchAllUsers, + ConversationType, + addParticipant, + removeParticipant, + fetchMessages, + subscribeToSSE, +} from "@/lib/api"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { @@ -132,11 +141,7 @@ export default function ChatPage() { }); }, [isConnected, user, allUsers, currentRoom, joinRoom, setCurrentRoom]); - - - - - + // Fetch messages for current room useEffect(() => { if (!currentRoom) return; @@ -151,7 +156,6 @@ export default function ChatPage() { createdAt: msg.createdAt, })); - // Replace only this room's messages setMessages((prev) => { const others = prev.filter((m) => m.roomId !== currentRoom); return [...others, ...normalized]; @@ -162,9 +166,6 @@ export default function ChatPage() { }); }, [currentRoom, setMessages]); - - - // Fetch all users useEffect(() => { if (!user) return; @@ -177,6 +178,122 @@ export default function ChatPage() { .catch(console.error); }, [user]); + // ─── SSE: real-time group invite / removal ─────────────────────────────── +useEffect(() => { + if (!user?.userId) return; + + console.log("[SSE HOOK] Initializing for user:", user.userId); + + const cleanup = subscribeToSSE(user.userId, { + // Someone added this user to a group conversation + onGroupInvite: ({ conversation }) => { + console.log("[SSE HOOK] onGroupInvite TRIGGERED"); + + if (!conversation) { + console.warn("[SSE HOOK] groupInvite missing conversation payload"); + return; + } + + console.log("[SSE HOOK] groupInvite payload:", conversation); + + const convId: string = conversation._id ?? conversation.id; + const isGroup = conversation.type === "group"; + + console.log("[SSE HOOK] convId:", convId, "isGroup:", isGroup); + + // Resolve display name + let roomName = conversation.name || "Unnamed Group"; + + if (!isGroup) { + const other = (conversation.participants ?? []).find( + (p: any) => (p._id ?? p) !== user.userId + ); + roomName = other?.name ?? "Direct Message"; + } + + console.log("[SSE HOOK] roomName resolved:", roomName); + + // Hydrate participants array + const participants: { _id: string; name: string }[] = ( + conversation.participants ?? [] + ).map((p: any) => + typeof p === "object" + ? { _id: p._id, name: p.name ?? p._id } + : { _id: p, name: p } + ); + + console.log("[SSE HOOK] participants:", participants); + + const newRoom: Room = { + id: convId, + name: roomName, + isGroup, + participants, + }; + + console.log("[SSE HOOK] newRoom created:", newRoom); + + // Add to sidebar if not already there + setRooms((prev) => { + const exists = prev.find((r) => r.id === convId); + if (exists) { + console.log("[SSE HOOK] room already exists, skipping add"); + return prev; + } + + console.log("[SSE HOOK] adding new room to sidebar"); + return [...prev, newRoom]; + }); + + console.log("[SSE HOOK] joining room:", convId); + joinRoom(convId); + }, + + // This user was removed from a group + onGroupRemoved: ({ conversation }) => { + console.log("[SSE HOOK] onGroupRemoved TRIGGERED"); + + if (!conversation) { + console.warn("[SSE HOOK] groupRemoved missing conversation payload"); + return; + } + + console.log("[SSE HOOK] groupRemoved payload:", conversation); + + const roomId = conversation._id ?? conversation.id; + + console.log("[SSE HOOK] removing roomId:", roomId); + + // Leave socket room + leaveRoom(roomId); + console.log("[SSE HOOK] left room:", roomId); + + // Remove from sidebar + setRooms((prev) => { + const filtered = prev.filter((r) => r.id !== roomId); + console.log("[SSE HOOK] updated rooms after removal:", filtered); + return filtered; + }); + + // Clear selection if the removed room was active + if (currentRoom === roomId) { + console.log("[SSE HOOK] clearing current room selection"); + setCurrentRoom(null); + } + + // Drop its messages from state + setMessages((prev) => { + const filtered = prev.filter((m) => m.roomId !== roomId); + console.log("[SSE HOOK] messages cleaned for room:", roomId); + return filtered; + }); + }, + }); + + return cleanup; +}, [user?.userId, joinRoom, leaveRoom, setCurrentRoom, setMessages]); + + // React to P2P Ready event useEffect(() => { if (!p2pReady) return; @@ -294,14 +411,12 @@ export default function ChatPage() { try { const updated = await addParticipant(convId, userId); - // Normalize participants: if they're plain ID strings, hydrate with names from allUsers const rawParticipants: any[] = updated.participants || []; const hydrated = rawParticipants.map((p: any) => { if (typeof p === "string") { const found = allUsers.find((u) => u._id === p); return found ? { _id: found._id, name: found.name } : { _id: p, name: p }; } - // Already an object — but name might be missing, try to fill it in if (!p.name) { const found = allUsers.find((u) => u._id === (p._id ?? p)); return found ? { _id: found._id, name: found.name } : p; @@ -346,14 +461,11 @@ export default function ChatPage() { } }; - const formatDate = (dateStr: string) => { const date = new Date(dateStr); const today = new Date(); - const isToday = date.toDateString() === today.toDateString(); if (isToday) return "Today"; - return date.toLocaleDateString([], { day: "numeric", month: "short", @@ -391,7 +503,6 @@ export default function ChatPage() { const selectedRoom = rooms.find((r) => r.id === currentRoom); - // Participants for manage dropdown (current selected group room) const manageParticipants = selectedRoom?.participants ?? []; const addableUsers = allUsers.filter( (u) => !manageParticipants.some((p: any) => (p._id ?? p) === u._id) @@ -399,7 +510,7 @@ export default function ChatPage() { return (