From 9e05f941abf915e66288eb25bf7435265eaab872 Mon Sep 17 00:00:00 2001 From: Gal Podlipnik Date: Thu, 12 Jun 2025 16:04:27 +0200 Subject: [PATCH] remove msg --- backend/src/services/messageService.ts | 41 +++++++++++++++++++++++++ backend/src/socket/socketHandlers.ts | 20 ++++++++++++ frontend/src/components/ChatApp.tsx | 4 +-- frontend/src/components/MessageCard.tsx | 26 +++++++++++++++- frontend/src/components/MessageList.tsx | 21 ++++++++++++- frontend/src/stores/chatStore.ts | 16 ++++++++++ 6 files changed, 124 insertions(+), 4 deletions(-) diff --git a/backend/src/services/messageService.ts b/backend/src/services/messageService.ts index 0a5f95d..5029a77 100644 --- a/backend/src/services/messageService.ts +++ b/backend/src/services/messageService.ts @@ -155,4 +155,45 @@ export class MessageService { }; } } + + static async deleteMessage(userId: string, messageId: string, roomId: string): Promise { + try { + const message = await prisma.message.findFirst({ + where: { + id: messageId, + userId: userId, + roomId: roomId, + }, + }); + + if (!message) { + return { + success: false, + error: "Message not found or you don't have permission to delete it", + status: 403, + }; + } + + await prisma.message.delete({ + where: { + id: messageId, + }, + }); + + return { + success: true, + data: { + messageId, + roomId, + }, + }; + } catch (error) { + console.error("Delete message error:", error); + return { + success: false, + error: "Failed to delete message", + status: 500, + }; + } + } } diff --git a/backend/src/socket/socketHandlers.ts b/backend/src/socket/socketHandlers.ts index 5df8586..bfcf801 100644 --- a/backend/src/socket/socketHandlers.ts +++ b/backend/src/socket/socketHandlers.ts @@ -21,6 +21,8 @@ export const handleConnection = async (io: Server, socket: AuthenticatedSocket) socket.on("send_message", (data: SendMessageRequest) => handleSendMessage(io, socket, data)); + socket.on("delete_message", (data) => handleDeleteMessage(io, socket, data)); + socket.on("react_to_message", (data: ReactToMessageRequest) => handleReactToMessage(io, socket, data)); socket.on("typing_start", (data: { roomId: string; username: string }) => handleTypingStart(io, socket, data)); @@ -139,6 +141,24 @@ const handleSendMessage = async (io: Server, socket: AuthenticatedSocket, data: } }; +const handleDeleteMessage = async (io: Server, socket: AuthenticatedSocket, data: { messageId: string; roomId: string }) => { + try { + const result = await MessageService.deleteMessage(socket.userId, data.messageId, data.roomId); + + if (result.success) { + io.to(data.roomId).emit("message_deleted", { + messageId: data.messageId, + roomId: data.roomId, + }); + } else { + socket.emit("error", { message: result.error }); + } + } catch (error) { + console.error(`Error handling delete message: ${error}`); + socket.emit("error", { message: "Failed to delete message" }); + } +}; + const handleReactToMessage = async (io: Server, socket: AuthenticatedSocket, data: ReactToMessageRequest) => { try { const result = await MessageService.reactToMessage(socket.userId, data); diff --git a/frontend/src/components/ChatApp.tsx b/frontend/src/components/ChatApp.tsx index 6181270..5263b3b 100644 --- a/frontend/src/components/ChatApp.tsx +++ b/frontend/src/components/ChatApp.tsx @@ -44,7 +44,7 @@ export const ChatApp: FC = () => { ) : ( -
+

Welcome to Chat App @@ -57,4 +57,4 @@ export const ChatApp: FC = () => {

); -}; \ No newline at end of file +}; diff --git a/frontend/src/components/MessageCard.tsx b/frontend/src/components/MessageCard.tsx index bb93183..7eec5ef 100644 --- a/frontend/src/components/MessageCard.tsx +++ b/frontend/src/components/MessageCard.tsx @@ -2,8 +2,9 @@ import { cn } from '@/lib/utils'; import { useAuthStore } from '@/stores/authStore'; import { useSocketStore } from '@/stores/socketStore'; import type { Message, MessageReaction } from '@/types'; -import { Heart, Smile, ThumbsUp } from 'lucide-react'; +import { Heart, Smile, ThumbsUp, Trash2 } from 'lucide-react'; import { useMemo, useRef, useState, type FC } from 'react'; +import { toast } from 'sonner'; import { Button } from './ui/button'; import { Popover, PopoverContent, PopoverTrigger } from './ui/popover'; @@ -27,6 +28,19 @@ export const MessageCard: FC<{ message: Message }> = ({ message }) => { return reactions.some((r) => r.type === type && r.userId === user?.id); }; + const handleDeleteMessage = () => { + if (confirm('Are you sure you want to delete this message?')) { + if (socket) { + socket.emit('delete_message', { + messageId: message.id, + roomId: message.roomId, + }); + setShowReactions(false); + toast.info('Message deleted successfully'); + } + } + }; + const formatTime = (dateString: string) => { return new Date(dateString).toLocaleTimeString([], { hour: '2-digit', @@ -157,6 +171,16 @@ export const MessageCard: FC<{ message: Message }> = ({ message }) => { > + {isCurrentUser && ( + + )}
diff --git a/frontend/src/components/MessageList.tsx b/frontend/src/components/MessageList.tsx index 0da8870..abfa03a 100644 --- a/frontend/src/components/MessageList.tsx +++ b/frontend/src/components/MessageList.tsx @@ -14,7 +14,7 @@ type MessageListProps = { }; export const MessageList: FC = ({ roomId }) => { - const { messages, setMessages } = useChatStore(); + const { messages, setMessages, removeMessage } = useChatStore(); const { socket } = useSocketStore(); const { user } = useAuthStore(); const [lastMessageId, setLastMessageId] = useState(null); @@ -96,6 +96,25 @@ export const MessageList: FC = ({ roomId }) => { } }, [socket, user, roomId, roomMessages]); + useEffect(() => { + if (!socket) return; + + const handleMessageDeleted = (data: { + messageId: string; + roomId: string; + }) => { + if (data.roomId === roomId) { + removeMessage(data.messageId, data.roomId); + } + }; + + socket.on('message_deleted', handleMessageDeleted); + + return () => { + socket.off('message_deleted', handleMessageDeleted); + }; + }, [socket, roomId, removeMessage]); + if (isLoading) { return (
diff --git a/frontend/src/stores/chatStore.ts b/frontend/src/stores/chatStore.ts index b5d8124..8cf5a20 100644 --- a/frontend/src/stores/chatStore.ts +++ b/frontend/src/stores/chatStore.ts @@ -16,6 +16,7 @@ type ChatState = { setSelectedRoom: (roomId: string | null) => void; setMessages: (roomId: string, messages: Message[]) => void; addMessage: (message: Message) => void; + removeMessage: (messageId: string, roomId: string) => void; updateMessageReactions: ( messageId: string, reactions: MessageReaction[] @@ -73,6 +74,21 @@ export const useChatStore = create()( })); }, + removeMessage: (messageId, roomId) => { + set((state) => { + if (!state.messages[roomId]) return state; + + return { + messages: { + ...state.messages, + [roomId]: state.messages[roomId].filter( + (message) => message.id !== messageId + ), + }, + }; + }); + }, + updateMessageReactions: (messageId, reactions) => { set((state) => { const newMessages = { ...state.messages };