feat: implement conversation mute and favorite functionality with corresponding notification suppression.

This commit is contained in:
pradeepkumar
2026-03-19 17:04:02 +05:30
parent 870eb0c067
commit fcf90e8638
4 changed files with 108 additions and 0 deletions

View File

@@ -488,6 +488,12 @@ model Conversation {
userUnreadCount Int @default(0) // Unread count for the user userUnreadCount Int @default(0) // Unread count for the user
agentUnreadCount Int @default(0) // Unread count for the agent agentUnreadCount Int @default(0) // Unread count for the agent
// Mute & Favorite (per-user)
userMuted Boolean @default(false)
agentMuted Boolean @default(false)
userFavorited Boolean @default(false)
agentFavorited Boolean @default(false)
// Timestamps // Timestamps
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt

View File

@@ -218,6 +218,36 @@ export class MessagesController {
return this.messagesService.markMessagesAsRead(conversationId, userId); return this.messagesService.markMessagesAsRead(conversationId, userId);
} }
// ==========================================
// TOGGLE MUTE
// ==========================================
@Patch('conversations/:id/mute')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Mute or unmute conversation notifications' })
@ApiParam({ name: 'id', description: 'Conversation ID' })
async toggleMute(
@Param('id') conversationId: string,
@CurrentUser('id') userId: string,
@Body() dto: { muted: boolean },
) {
return this.messagesService.toggleMute(conversationId, userId, dto.muted);
}
// ==========================================
// TOGGLE FAVORITE
// ==========================================
@Patch('conversations/:id/favorite')
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: 'Star or unstar a conversation' })
@ApiParam({ name: 'id', description: 'Conversation ID' })
async toggleFavorite(
@Param('id') conversationId: string,
@CurrentUser('id') userId: string,
@Body() dto: { favorited: boolean },
) {
return this.messagesService.toggleFavorite(conversationId, userId, dto.favorited);
}
// ========================================== // ==========================================
// CLEAR CHAT (delete messages, keep conversation) // CLEAR CHAT (delete messages, keep conversation)
// ========================================== // ==========================================

View File

@@ -722,4 +722,64 @@ export class MessagesService {
return result._sum.userUnreadCount || 0; return result._sum.userUnreadCount || 0;
} }
} }
/**
* Toggle mute status for a conversation
*/
async toggleMute(conversationId: string, userId: string, muted: boolean) {
const conversation = await this.prisma.conversation.findUnique({
where: { id: conversationId },
include: { agentProfile: { select: { userId: true } } },
});
if (!conversation) throw new NotFoundException('Conversation not found');
const isUser = conversation.userId === userId;
const isAgent = conversation.agentProfile.userId === userId;
if (!isUser && !isAgent) throw new ForbiddenException('You are not part of this conversation');
const updated = await this.prisma.conversation.update({
where: { id: conversationId },
data: isUser ? { userMuted: muted } : { agentMuted: muted },
});
return { id: updated.id, muted: isUser ? updated.userMuted : updated.agentMuted };
}
/**
* Toggle favorite status for a conversation
*/
async toggleFavorite(conversationId: string, userId: string, favorited: boolean) {
const conversation = await this.prisma.conversation.findUnique({
where: { id: conversationId },
include: { agentProfile: { select: { userId: true } } },
});
if (!conversation) throw new NotFoundException('Conversation not found');
const isUser = conversation.userId === userId;
const isAgent = conversation.agentProfile.userId === userId;
if (!isUser && !isAgent) throw new ForbiddenException('You are not part of this conversation');
const updated = await this.prisma.conversation.update({
where: { id: conversationId },
data: isUser ? { userFavorited: favorited } : { agentFavorited: favorited },
});
return { id: updated.id, favorited: isUser ? updated.userFavorited : updated.agentFavorited };
}
/**
* Check if a conversation is muted for a specific user
*/
async isConversationMuted(conversationId: string, userId: string): Promise<boolean> {
const conversation = await this.prisma.conversation.findUnique({
where: { id: conversationId },
include: { agentProfile: { select: { userId: true } } },
});
if (!conversation) return false;
const isUser = conversation.userId === userId;
return isUser ? conversation.userMuted : conversation.agentMuted;
}
} }

View File

@@ -347,6 +347,18 @@ export class NotificationsService implements OnModuleInit {
messagePreview: string; messagePreview: string;
conversationId: string; conversationId: string;
}) { }) {
// Check if the recipient has muted this conversation
const conversation = await this.prisma.conversation.findUnique({
where: { id: event.conversationId },
include: { agentProfile: { select: { userId: true } } },
});
if (conversation) {
const isUser = conversation.userId === event.recipientUserId;
const isMuted = isUser ? conversation.userMuted : conversation.agentMuted;
if (isMuted) return; // Skip notification for muted conversations
}
const prefix = await this.getRoutePrefix(event.recipientUserId); const prefix = await this.getRoutePrefix(event.recipientUserId);
const actionUrl = `${prefix}/message?conversationId=${event.conversationId}`; const actionUrl = `${prefix}/message?conversationId=${event.conversationId}`;