feat(dashboard): add unread count for match chats
Track unread messages in match chats and display count badge: - Schema: Add user1LastReadAt/user2LastReadAt to Match model - Backend: Calculate unreadCount in dashboard API - Socket: Update lastReadAt when user joins match room - Frontend: Display red badge with unread count on match avatar
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "matches" ADD COLUMN "user1_last_read_at" TIMESTAMP(3),
|
||||
ADD COLUMN "user2_last_read_at" TIMESTAMP(3),
|
||||
ALTER COLUMN "slug" DROP DEFAULT;
|
||||
@@ -134,14 +134,16 @@ model Message {
|
||||
|
||||
// Matches (pairs of users for collaboration)
|
||||
model Match {
|
||||
id Int @id @default(autoincrement())
|
||||
slug String @unique @default(cuid()) @db.VarChar(50)
|
||||
user1Id Int @map("user1_id")
|
||||
user2Id Int @map("user2_id")
|
||||
eventId Int @map("event_id")
|
||||
roomId Int? @map("room_id")
|
||||
status String @default("pending") @db.VarChar(20) // 'pending', 'accepted', 'completed'
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
id Int @id @default(autoincrement())
|
||||
slug String @unique @default(cuid()) @db.VarChar(50)
|
||||
user1Id Int @map("user1_id")
|
||||
user2Id Int @map("user2_id")
|
||||
eventId Int @map("event_id")
|
||||
roomId Int? @map("room_id")
|
||||
status String @default("pending") @db.VarChar(20) // 'pending', 'accepted', 'completed'
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
user1LastReadAt DateTime? @map("user1_last_read_at")
|
||||
user2LastReadAt DateTime? @map("user2_last_read_at")
|
||||
|
||||
// Relations
|
||||
user1 User @relation("MatchUser1", fields: [user1Id], references: [id])
|
||||
|
||||
@@ -180,6 +180,14 @@ router.get('/', authenticate, async (req, res, next) => {
|
||||
const lastMessage = match.room?.messages[0];
|
||||
const lastMessageAt = lastMessage ? lastMessage.createdAt : match.createdAt;
|
||||
|
||||
// Calculate unread count
|
||||
const myLastReadAt = isUser1 ? match.user1LastReadAt : match.user2LastReadAt;
|
||||
const unreadCount = myLastReadAt
|
||||
? (match.room?.messages || []).filter(
|
||||
(m) => m.userId !== userId && new Date(m.createdAt) > new Date(myLastReadAt)
|
||||
).length
|
||||
: (match.room?.messages || []).filter((m) => m.userId !== userId).length;
|
||||
|
||||
return {
|
||||
id: match.id,
|
||||
slug: match.slug,
|
||||
@@ -197,6 +205,7 @@ router.get('/', authenticate, async (req, res, next) => {
|
||||
},
|
||||
videoExchange,
|
||||
ratings,
|
||||
unreadCount,
|
||||
lastMessageAt,
|
||||
status: match.status,
|
||||
};
|
||||
|
||||
@@ -268,11 +268,35 @@ function initializeSocket(httpServer) {
|
||||
});
|
||||
|
||||
// Join private match room
|
||||
socket.on('join_match_room', ({ matchId }) => {
|
||||
const roomName = `match_${matchId}`;
|
||||
socket.join(roomName);
|
||||
socket.currentMatchRoom = roomName;
|
||||
console.log(`👥 ${socket.user.username} joined match room ${matchId}`);
|
||||
socket.on('join_match_room', async ({ matchId }) => {
|
||||
try {
|
||||
const roomName = `match_${matchId}`;
|
||||
socket.join(roomName);
|
||||
socket.currentMatchRoom = roomName;
|
||||
socket.currentMatchId = parseInt(matchId);
|
||||
console.log(`👥 ${socket.user.username} joined match room ${matchId}`);
|
||||
|
||||
// Mark messages as read by updating lastReadAt
|
||||
const match = await prisma.match.findUnique({
|
||||
where: { id: parseInt(matchId) },
|
||||
select: { user1Id: true, user2Id: true },
|
||||
});
|
||||
|
||||
if (match) {
|
||||
const isUser1 = match.user1Id === socket.user.id;
|
||||
const updateData = isUser1
|
||||
? { user1LastReadAt: new Date() }
|
||||
: { user2LastReadAt: new Date() };
|
||||
|
||||
await prisma.match.update({
|
||||
where: { id: parseInt(matchId) },
|
||||
data: updateData,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Join match room error:', error);
|
||||
socket.emit('error', { message: 'Failed to join match room' });
|
||||
}
|
||||
});
|
||||
|
||||
// Send message to match room
|
||||
|
||||
Reference in New Issue
Block a user