remove msg
This commit is contained in:
parent
95757dd169
commit
9e05f941ab
@ -155,4 +155,45 @@ export class MessageService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async deleteMessage(userId: string, messageId: string, roomId: string): Promise<ApiResponse> {
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,6 +21,8 @@ export const handleConnection = async (io: Server, socket: AuthenticatedSocket)
|
|||||||
|
|
||||||
socket.on("send_message", (data: SendMessageRequest) => handleSendMessage(io, socket, data));
|
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("react_to_message", (data: ReactToMessageRequest) => handleReactToMessage(io, socket, data));
|
||||||
|
|
||||||
socket.on("typing_start", (data: { roomId: string; username: string }) => handleTypingStart(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) => {
|
const handleReactToMessage = async (io: Server, socket: AuthenticatedSocket, data: ReactToMessageRequest) => {
|
||||||
try {
|
try {
|
||||||
const result = await MessageService.reactToMessage(socket.userId, data);
|
const result = await MessageService.reactToMessage(socket.userId, data);
|
||||||
|
|||||||
@ -44,7 +44,7 @@ export const ChatApp: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex-1 flex items-center justify-center text-gray-500">
|
<div className="flex-1 flex items-center justify-center text-gray-500 dark:bg-gray-800 dark:text-gray-400">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<h2 className="text-xl font-semibold mb-2">
|
<h2 className="text-xl font-semibold mb-2">
|
||||||
Welcome to Chat App
|
Welcome to Chat App
|
||||||
@ -57,4 +57,4 @@ export const ChatApp: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,8 +2,9 @@ import { cn } from '@/lib/utils';
|
|||||||
import { useAuthStore } from '@/stores/authStore';
|
import { useAuthStore } from '@/stores/authStore';
|
||||||
import { useSocketStore } from '@/stores/socketStore';
|
import { useSocketStore } from '@/stores/socketStore';
|
||||||
import type { Message, MessageReaction } from '@/types';
|
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 { useMemo, useRef, useState, type FC } from 'react';
|
||||||
|
import { toast } from 'sonner';
|
||||||
import { Button } from './ui/button';
|
import { Button } from './ui/button';
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from './ui/popover';
|
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);
|
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) => {
|
const formatTime = (dateString: string) => {
|
||||||
return new Date(dateString).toLocaleTimeString([], {
|
return new Date(dateString).toLocaleTimeString([], {
|
||||||
hour: '2-digit',
|
hour: '2-digit',
|
||||||
@ -157,6 +171,16 @@ export const MessageCard: FC<{ message: Message }> = ({ message }) => {
|
|||||||
>
|
>
|
||||||
<Smile className="h-5 w-5 text-yellow-500" />
|
<Smile className="h-5 w-5 text-yellow-500" />
|
||||||
</Button>
|
</Button>
|
||||||
|
{isCurrentUser && (
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="ghost"
|
||||||
|
className="rounded-full p-2"
|
||||||
|
onClick={handleDeleteMessage}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-5 w-5 text-gray-500" />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -14,7 +14,7 @@ type MessageListProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const MessageList: FC<MessageListProps> = ({ roomId }) => {
|
export const MessageList: FC<MessageListProps> = ({ roomId }) => {
|
||||||
const { messages, setMessages } = useChatStore();
|
const { messages, setMessages, removeMessage } = useChatStore();
|
||||||
const { socket } = useSocketStore();
|
const { socket } = useSocketStore();
|
||||||
const { user } = useAuthStore();
|
const { user } = useAuthStore();
|
||||||
const [lastMessageId, setLastMessageId] = useState<string | null>(null);
|
const [lastMessageId, setLastMessageId] = useState<string | null>(null);
|
||||||
@ -96,6 +96,25 @@ export const MessageList: FC<MessageListProps> = ({ roomId }) => {
|
|||||||
}
|
}
|
||||||
}, [socket, user, roomId, roomMessages]);
|
}, [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) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 flex items-center justify-center dark:bg-gray-900">
|
<div className="flex-1 flex items-center justify-center dark:bg-gray-900">
|
||||||
|
|||||||
@ -16,6 +16,7 @@ type ChatState = {
|
|||||||
setSelectedRoom: (roomId: string | null) => void;
|
setSelectedRoom: (roomId: string | null) => void;
|
||||||
setMessages: (roomId: string, messages: Message[]) => void;
|
setMessages: (roomId: string, messages: Message[]) => void;
|
||||||
addMessage: (message: Message) => void;
|
addMessage: (message: Message) => void;
|
||||||
|
removeMessage: (messageId: string, roomId: string) => void;
|
||||||
updateMessageReactions: (
|
updateMessageReactions: (
|
||||||
messageId: string,
|
messageId: string,
|
||||||
reactions: MessageReaction[]
|
reactions: MessageReaction[]
|
||||||
@ -73,6 +74,21 @@ export const useChatStore = create<ChatState>()(
|
|||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
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) => {
|
updateMessageReactions: (messageId, reactions) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const newMessages = { ...state.messages };
|
const newMessages = { ...state.messages };
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user