real time messaging
This commit is contained in:
@@ -3,11 +3,13 @@ import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { Message } from '../types';
|
||||
import { messageAPI } from '../services/api';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { useSocket } from '../contexts/SocketContext';
|
||||
|
||||
const MessageDetail: React.FC = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const navigate = useNavigate();
|
||||
const { user } = useAuth();
|
||||
const { isConnected, onNewMessage } = useSocket();
|
||||
const [message, setMessage] = useState<Message | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -18,6 +20,34 @@ const MessageDetail: React.FC = () => {
|
||||
fetchMessage();
|
||||
}, [id]);
|
||||
|
||||
// Listen for new replies in real-time
|
||||
useEffect(() => {
|
||||
if (!isConnected || !message) return;
|
||||
|
||||
const cleanup = onNewMessage((newMessage: Message) => {
|
||||
// Check if this is a reply to the current thread
|
||||
if (newMessage.parentMessageId === message.id) {
|
||||
setMessage((prevMessage) => {
|
||||
if (!prevMessage) return prevMessage;
|
||||
|
||||
// Check if reply already exists (avoid duplicates)
|
||||
const replies = prevMessage.replies || [];
|
||||
if (replies.some(r => r.id === newMessage.id)) {
|
||||
return prevMessage;
|
||||
}
|
||||
|
||||
// Add new reply to the thread
|
||||
return {
|
||||
...prevMessage,
|
||||
replies: [...replies, newMessage]
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return cleanup;
|
||||
}, [isConnected, message?.id, onNewMessage]);
|
||||
|
||||
const fetchMessage = async () => {
|
||||
try {
|
||||
const response = await messageAPI.getMessage(id!);
|
||||
@@ -38,7 +68,7 @@ const MessageDetail: React.FC = () => {
|
||||
|
||||
try {
|
||||
const recipientId = message.senderId === user?.id ? message.receiverId : message.senderId;
|
||||
await messageAPI.sendMessage({
|
||||
const response = await messageAPI.sendMessage({
|
||||
receiverId: recipientId,
|
||||
subject: `Re: ${message.subject}`,
|
||||
content: replyContent,
|
||||
@@ -46,7 +76,20 @@ const MessageDetail: React.FC = () => {
|
||||
});
|
||||
|
||||
setReplyContent('');
|
||||
fetchMessage(); // Refresh to show the new reply
|
||||
|
||||
// Note: Socket will automatically add the reply to the thread
|
||||
// But we add it manually for immediate feedback if socket is disconnected
|
||||
if (!isConnected) {
|
||||
setMessage((prevMessage) => {
|
||||
if (!prevMessage) return prevMessage;
|
||||
const replies = prevMessage.replies || [];
|
||||
return {
|
||||
...prevMessage,
|
||||
replies: [...replies, response.data]
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
alert('Reply sent successfully!');
|
||||
} catch (err: any) {
|
||||
setError(err.response?.data?.error || 'Failed to send reply');
|
||||
|
||||
@@ -3,11 +3,13 @@ import { useNavigate } from 'react-router-dom';
|
||||
import { Message, User } from '../types';
|
||||
import { messageAPI } from '../services/api';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { useSocket } from '../contexts/SocketContext';
|
||||
import ChatWindow from '../components/ChatWindow';
|
||||
|
||||
const Messages: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const { user } = useAuth();
|
||||
const { isConnected, onNewMessage } = useSocket();
|
||||
const [messages, setMessages] = useState<Message[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -18,6 +20,27 @@ const Messages: React.FC = () => {
|
||||
fetchMessages();
|
||||
}, []);
|
||||
|
||||
// Listen for new messages in real-time
|
||||
useEffect(() => {
|
||||
if (!isConnected) return;
|
||||
|
||||
const cleanup = onNewMessage((newMessage: Message) => {
|
||||
// Only add if this is a received message (user is the receiver)
|
||||
if (newMessage.receiverId === user?.id) {
|
||||
setMessages((prevMessages) => {
|
||||
// Check if message already exists (avoid duplicates)
|
||||
if (prevMessages.some(m => m.id === newMessage.id)) {
|
||||
return prevMessages;
|
||||
}
|
||||
// Add new message to the top of the inbox
|
||||
return [newMessage, ...prevMessages];
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return cleanup;
|
||||
}, [isConnected, user?.id, onNewMessage]);
|
||||
|
||||
const fetchMessages = async () => {
|
||||
try {
|
||||
const response = await messageAPI.getMessages();
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { User, Item } from '../types';
|
||||
import { userAPI, itemAPI } from '../services/api';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import ChatWindow from '../components/ChatWindow';
|
||||
|
||||
const PublicProfile: React.FC = () => {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
@@ -12,6 +13,7 @@ const PublicProfile: React.FC = () => {
|
||||
const [items, setItems] = useState<Item[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [showChat, setShowChat] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchUserProfile();
|
||||
@@ -85,11 +87,10 @@ const PublicProfile: React.FC = () => {
|
||||
</div>
|
||||
)}
|
||||
<h3>{user.firstName} {user.lastName}</h3>
|
||||
<p className="text-muted">@{user.username}</p>
|
||||
{currentUser && currentUser.id !== user.id && (
|
||||
<button
|
||||
className="btn btn-primary mt-3"
|
||||
onClick={() => navigate('/messages')}
|
||||
<button
|
||||
className="btn btn-primary mt-3"
|
||||
onClick={() => setShowChat(true)}
|
||||
>
|
||||
<i className="bi bi-chat-dots-fill me-2"></i>Message
|
||||
</button>
|
||||
@@ -148,6 +149,15 @@ const PublicProfile: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* ChatWindow popup */}
|
||||
{user && (
|
||||
<ChatWindow
|
||||
show={showChat}
|
||||
onClose={() => setShowChat(false)}
|
||||
recipient={user}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user