From 78133497b3246d2e9aa9d61dc4461008e4b0d05f Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Fri, 10 Apr 2026 16:48:12 +0530 Subject: [PATCH] feat: implement per-user soft delete for conversations with automatic restoration on new messages --- prisma/schema.prisma | 4 +++ src/messages/messages.service.ts | 42 ++++++++++++++++++++++++++------ 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5ce7733..c91da21 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -507,6 +507,10 @@ model Conversation { userClearedAt DateTime? agentClearedAt DateTime? + // Soft delete (per-user) — conversation hidden for that user until a new message arrives + userDeletedAt DateTime? + agentDeletedAt DateTime? + // Timestamps createdAt DateTime @default(now()) updatedAt DateTime @updatedAt diff --git a/src/messages/messages.service.ts b/src/messages/messages.service.ts index 1f3492b..97a6dba 100644 --- a/src/messages/messages.service.ts +++ b/src/messages/messages.service.ts @@ -140,6 +140,21 @@ export class MessagesService { }, include: conversationInclude, }); + } else { + // Clear soft-delete flag for the caller so the conversation reappears + const needsClear = callerRole === UserRole.AGENT + ? conversation.agentDeletedAt !== null + : conversation.userDeletedAt !== null; + + if (needsClear) { + conversation = await this.prisma.conversation.update({ + where: { id: conversation.id }, + data: callerRole === UserRole.AGENT + ? { agentDeletedAt: null } + : { userDeletedAt: null }, + include: conversationInclude, + }); + } } // Transform to include otherParty based on caller role @@ -275,6 +290,9 @@ export class MessagesService { ...(isUser ? { agentUnreadCount: { increment: 1 } } : { userUnreadCount: { increment: 1 } }), + // Clear soft-delete flags so conversation reappears for both parties + userDeletedAt: null, + agentDeletedAt: null, }, }); @@ -315,7 +333,10 @@ export class MessagesService { } conversations = await this.prisma.conversation.findMany({ - where: { agentProfileId: agentProfile.id }, + where: { + agentProfileId: agentProfile.id, + agentDeletedAt: null, // Exclude soft-deleted conversations + }, include: { user: { select: { @@ -362,7 +383,10 @@ export class MessagesService { } else { // Regular user conversations = await this.prisma.conversation.findMany({ - where: { userId }, + where: { + userId, + userDeletedAt: null, // Exclude soft-deleted conversations + }, include: { agentProfile: { select: { @@ -781,13 +805,17 @@ export class MessagesService { throw new ForbiddenException('You are not part of this conversation'); } - // Delete all messages first, then the conversation - await this.prisma.message.deleteMany({ - where: { conversationId }, - }); + // Soft delete: mark conversation as deleted for this user only + // The other party still sees the conversation and all messages. + // If a new message arrives later, the deletedAt flag is cleared so + // the conversation reappears for the user who deleted it. + const now = new Date(); - await this.prisma.conversation.delete({ + await this.prisma.conversation.update({ where: { id: conversationId }, + data: isUser + ? { userDeletedAt: now, userClearedAt: now, userUnreadCount: 0 } + : { agentDeletedAt: now, agentClearedAt: now, agentUnreadCount: 0 }, }); return null;