removed console logs from frontend and a logs from locationService
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
const { sequelize } = require('../models');
|
||||
const { QueryTypes } = require('sequelize');
|
||||
const { sequelize } = require("../models");
|
||||
const { QueryTypes } = require("sequelize");
|
||||
|
||||
class LocationService {
|
||||
/**
|
||||
@@ -13,19 +13,13 @@ class LocationService {
|
||||
*/
|
||||
async findUsersInRadius(latitude, longitude, radiusMiles = 10) {
|
||||
if (!latitude || !longitude) {
|
||||
throw new Error('Latitude and longitude are required');
|
||||
throw new Error("Latitude and longitude are required");
|
||||
}
|
||||
|
||||
if (radiusMiles <= 0 || radiusMiles > 100) {
|
||||
throw new Error('Radius must be between 1 and 100 miles');
|
||||
throw new Error("Radius must be between 1 and 100 miles");
|
||||
}
|
||||
|
||||
console.log('Finding users in radius:', {
|
||||
centerLatitude: latitude,
|
||||
centerLongitude: longitude,
|
||||
radiusMiles
|
||||
});
|
||||
|
||||
try {
|
||||
// Haversine formula:
|
||||
// distance = 3959 * acos(cos(radians(lat1)) * cos(radians(lat2))
|
||||
@@ -62,29 +56,22 @@ class LocationService {
|
||||
replacements: {
|
||||
lat: parseFloat(latitude),
|
||||
lng: parseFloat(longitude),
|
||||
radiusMiles: parseFloat(radiusMiles)
|
||||
radiusMiles: parseFloat(radiusMiles),
|
||||
},
|
||||
type: QueryTypes.SELECT
|
||||
type: QueryTypes.SELECT,
|
||||
});
|
||||
|
||||
console.log('Users found in radius:', users.map(u => ({
|
||||
id: u.id,
|
||||
userLat: u.latitude,
|
||||
userLng: u.longitude,
|
||||
distance: parseFloat(u.distance).toFixed(2)
|
||||
})));
|
||||
|
||||
return users.map(user => ({
|
||||
return users.map((user) => ({
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
latitude: parseFloat(user.latitude),
|
||||
longitude: parseFloat(user.longitude),
|
||||
distance: parseFloat(user.distance).toFixed(2) // Round to 2 decimal places
|
||||
distance: parseFloat(user.distance).toFixed(2), // Round to 2 decimal places
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('Error finding users in radius:', error);
|
||||
console.error("Error finding users in radius:", error);
|
||||
throw new Error(`Failed to find users in radius: ${error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -105,8 +92,10 @@ class LocationService {
|
||||
|
||||
const a =
|
||||
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||
Math.cos(this.toRadians(lat1)) * Math.cos(this.toRadians(lat2)) *
|
||||
Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
||||
Math.cos(this.toRadians(lat1)) *
|
||||
Math.cos(this.toRadians(lat2)) *
|
||||
Math.sin(dLon / 2) *
|
||||
Math.sin(dLon / 2);
|
||||
|
||||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
const distance = R * c;
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import React, { useState, useEffect, useLayoutEffect, useRef, useCallback } from 'react';
|
||||
import { messageAPI, getMessageImageUrl } from '../services/api';
|
||||
import { User, Message } from '../types';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { useSocket } from '../contexts/SocketContext';
|
||||
import TypingIndicator from './TypingIndicator';
|
||||
import React, {
|
||||
useState,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useRef,
|
||||
useCallback,
|
||||
} from "react";
|
||||
import { messageAPI, getMessageImageUrl } from "../services/api";
|
||||
import { User, Message } from "../types";
|
||||
import { useAuth } from "../contexts/AuthContext";
|
||||
import { useSocket } from "../contexts/SocketContext";
|
||||
import TypingIndicator from "./TypingIndicator";
|
||||
|
||||
interface ChatWindowProps {
|
||||
show: boolean;
|
||||
@@ -12,15 +18,30 @@ interface ChatWindowProps {
|
||||
onMessagesRead?: (partnerId: string, count: number) => void;
|
||||
}
|
||||
|
||||
const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMessagesRead }) => {
|
||||
const ChatWindow: React.FC<ChatWindowProps> = ({
|
||||
show,
|
||||
onClose,
|
||||
recipient,
|
||||
onMessagesRead,
|
||||
}) => {
|
||||
const { user: currentUser } = useAuth();
|
||||
const { isConnected, joinConversation, leaveConversation, onNewMessage, onUserTyping, emitTypingStart, emitTypingStop } = useSocket();
|
||||
const {
|
||||
isConnected,
|
||||
joinConversation,
|
||||
leaveConversation,
|
||||
onNewMessage,
|
||||
onUserTyping,
|
||||
emitTypingStart,
|
||||
emitTypingStop,
|
||||
} = useSocket();
|
||||
const [messages, setMessages] = useState<Message[]>([]);
|
||||
const [newMessage, setNewMessage] = useState('');
|
||||
const [newMessage, setNewMessage] = useState("");
|
||||
const [sending, setSending] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [isRecipientTyping, setIsRecipientTyping] = useState(false);
|
||||
const [initialUnreadMessageIds, setInitialUnreadMessageIds] = useState<Set<string>>(new Set());
|
||||
const [initialUnreadMessageIds, setInitialUnreadMessageIds] = useState<
|
||||
Set<string>
|
||||
>(new Set());
|
||||
const [isAtBottom, setIsAtBottom] = useState(true);
|
||||
const [hasScrolledToUnread, setHasScrolledToUnread] = useState(false);
|
||||
const [selectedImage, setSelectedImage] = useState<File | null>(null);
|
||||
@@ -52,59 +73,57 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
}, [show, recipient.id, isConnected]);
|
||||
|
||||
// Create a stable callback for handling new messages
|
||||
const handleNewMessage = useCallback(async (message: Message) => {
|
||||
console.log('[ChatWindow] Received new_message event:', message);
|
||||
|
||||
// Only add messages that are part of this conversation
|
||||
if (
|
||||
(message.senderId === recipient.id && message.receiverId === currentUser?.id) ||
|
||||
(message.senderId === currentUser?.id && message.receiverId === recipient.id)
|
||||
) {
|
||||
console.log('[ChatWindow] Message is for this conversation, adding to chat');
|
||||
setMessages((prevMessages) => {
|
||||
// Check if message already exists (avoid duplicates)
|
||||
if (prevMessages.some(m => m.id === message.id)) {
|
||||
console.log('[ChatWindow] Message already exists, skipping');
|
||||
return prevMessages;
|
||||
}
|
||||
console.log('[ChatWindow] Adding new message to chat');
|
||||
return [...prevMessages, message];
|
||||
});
|
||||
|
||||
// Mark incoming messages from recipient as read
|
||||
if (message.senderId === recipient.id && message.receiverId === currentUser?.id && !message.isRead) {
|
||||
console.log('[ChatWindow] Marking new incoming message as read');
|
||||
try {
|
||||
await messageAPI.markAsRead(message.id);
|
||||
|
||||
// Notify parent component that message was marked read
|
||||
if (onMessagesRead) {
|
||||
onMessagesRead(recipient.id, 1);
|
||||
const handleNewMessage = useCallback(
|
||||
async (message: Message) => {
|
||||
// Only add messages that are part of this conversation
|
||||
if (
|
||||
(message.senderId === recipient.id &&
|
||||
message.receiverId === currentUser?.id) ||
|
||||
(message.senderId === currentUser?.id &&
|
||||
message.receiverId === recipient.id)
|
||||
) {
|
||||
setMessages((prevMessages) => {
|
||||
// Check if message already exists (avoid duplicates)
|
||||
if (prevMessages.some((m) => m.id === message.id)) {
|
||||
return prevMessages;
|
||||
}
|
||||
return [...prevMessages, message];
|
||||
});
|
||||
|
||||
// Mark incoming messages from recipient as read
|
||||
if (
|
||||
message.senderId === recipient.id &&
|
||||
message.receiverId === currentUser?.id &&
|
||||
!message.isRead
|
||||
) {
|
||||
try {
|
||||
await messageAPI.markAsRead(message.id);
|
||||
|
||||
// Notify parent component that message was marked read
|
||||
if (onMessagesRead) {
|
||||
onMessagesRead(recipient.id, 1);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to mark message ${message.id} as read:`,
|
||||
error
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Failed to mark message ${message.id} as read:`, error);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log('[ChatWindow] Message not for this conversation, ignoring');
|
||||
}
|
||||
}, [recipient.id, currentUser?.id, onMessagesRead]);
|
||||
},
|
||||
[recipient.id, currentUser?.id, onMessagesRead]
|
||||
);
|
||||
|
||||
// Listen for new messages in real-time
|
||||
useEffect(() => {
|
||||
console.log('[ChatWindow] Message listener useEffect running', { isConnected, show, recipientId: recipient.id });
|
||||
|
||||
if (!isConnected || !show) {
|
||||
console.log('[ChatWindow] Skipping listener setup:', { isConnected, show });
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[ChatWindow] Setting up message listener for recipient:', recipient.id);
|
||||
|
||||
const cleanup = onNewMessage(handleNewMessage);
|
||||
|
||||
return () => {
|
||||
console.log('[ChatWindow] Cleaning up message listener for recipient:', recipient.id);
|
||||
cleanup();
|
||||
};
|
||||
}, [isConnected, show, onNewMessage, handleNewMessage]);
|
||||
@@ -143,21 +162,20 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
if (!loading && !hasScrolledToUnread && messages.length > 0) {
|
||||
if (initialUnreadMessageIds.size > 0) {
|
||||
// Find the oldest unread message
|
||||
const oldestUnread = messages.find(m => initialUnreadMessageIds.has(m.id));
|
||||
const oldestUnread = messages.find((m) =>
|
||||
initialUnreadMessageIds.has(m.id)
|
||||
);
|
||||
|
||||
if (oldestUnread && messageRefs.current.has(oldestUnread.id)) {
|
||||
console.log(`[ChatWindow] Scrolling to oldest unread message: ${oldestUnread.id}`);
|
||||
messageRefs.current.get(oldestUnread.id)?.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start'
|
||||
behavior: "smooth",
|
||||
block: "start",
|
||||
});
|
||||
} else {
|
||||
console.log('[ChatWindow] Unread message ref not found, scrolling to bottom');
|
||||
scrollToBottom();
|
||||
}
|
||||
} else {
|
||||
// No unread messages, scroll to bottom
|
||||
console.log('[ChatWindow] No unread messages, scrolling to bottom');
|
||||
scrollToBottom();
|
||||
}
|
||||
setHasScrolledToUnread(true);
|
||||
@@ -176,26 +194,33 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
// Fetch all messages between current user and recipient
|
||||
const [sentRes, receivedRes] = await Promise.all([
|
||||
messageAPI.getSentMessages(),
|
||||
messageAPI.getMessages()
|
||||
messageAPI.getMessages(),
|
||||
]);
|
||||
|
||||
const sentToRecipient = sentRes.data.filter((msg: Message) => msg.receiverId === recipient.id);
|
||||
const receivedFromRecipient = receivedRes.data.filter((msg: Message) => msg.senderId === recipient.id);
|
||||
const sentToRecipient = sentRes.data.filter(
|
||||
(msg: Message) => msg.receiverId === recipient.id
|
||||
);
|
||||
const receivedFromRecipient = receivedRes.data.filter(
|
||||
(msg: Message) => msg.senderId === recipient.id
|
||||
);
|
||||
|
||||
// Combine and sort by date
|
||||
const allMessages = [...sentToRecipient, ...receivedFromRecipient].sort(
|
||||
(a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
|
||||
(a, b) =>
|
||||
new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
|
||||
);
|
||||
|
||||
setMessages(allMessages);
|
||||
|
||||
// Mark all unread messages from recipient as read
|
||||
const unreadMessages = receivedFromRecipient.filter((msg: Message) => !msg.isRead);
|
||||
const unreadMessages = receivedFromRecipient.filter(
|
||||
(msg: Message) => !msg.isRead
|
||||
);
|
||||
if (unreadMessages.length > 0) {
|
||||
console.log(`[ChatWindow] Marking ${unreadMessages.length} messages as read`);
|
||||
|
||||
// Save unread message IDs for scrolling purposes
|
||||
setInitialUnreadMessageIds(new Set(unreadMessages.map((m: Message) => m.id)));
|
||||
setInitialUnreadMessageIds(
|
||||
new Set(unreadMessages.map((m: Message) => m.id))
|
||||
);
|
||||
|
||||
// Mark each message as read
|
||||
const markReadPromises = unreadMessages.map((message: Message) =>
|
||||
@@ -212,28 +237,32 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch messages:', error);
|
||||
console.error("Failed to fetch messages:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const scrollToBottom = () => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||
};
|
||||
|
||||
const setMessageRef = useCallback((id: string) => (el: HTMLDivElement | null) => {
|
||||
if (el) {
|
||||
messageRefs.current.set(id, el);
|
||||
} else {
|
||||
messageRefs.current.delete(id);
|
||||
}
|
||||
}, []);
|
||||
const setMessageRef = useCallback(
|
||||
(id: string) => (el: HTMLDivElement | null) => {
|
||||
if (el) {
|
||||
messageRefs.current.set(id, el);
|
||||
} else {
|
||||
messageRefs.current.delete(id);
|
||||
}
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleScroll = () => {
|
||||
if (!messagesContainerRef.current) return;
|
||||
|
||||
const { scrollTop, scrollHeight, clientHeight } = messagesContainerRef.current;
|
||||
const { scrollTop, scrollHeight, clientHeight } =
|
||||
messagesContainerRef.current;
|
||||
const isBottom = scrollHeight - scrollTop - clientHeight < 50; // 50px threshold
|
||||
setIsAtBottom(isBottom);
|
||||
};
|
||||
@@ -265,14 +294,14 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
const file = e.target.files?.[0];
|
||||
if (file) {
|
||||
// Validate file type
|
||||
if (!file.type.startsWith('image/')) {
|
||||
alert('Please select an image file');
|
||||
if (!file.type.startsWith("image/")) {
|
||||
alert("Please select an image file");
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate file size (5MB)
|
||||
if (file.size > 5 * 1024 * 1024) {
|
||||
alert('Image size must be less than 5MB');
|
||||
alert("Image size must be less than 5MB");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -291,7 +320,7 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
setSelectedImage(null);
|
||||
setImagePreview(null);
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
fileInputRef.current.value = "";
|
||||
}
|
||||
};
|
||||
|
||||
@@ -311,20 +340,20 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
setSending(true);
|
||||
const messageContent = newMessage;
|
||||
const imageToSend = selectedImage;
|
||||
setNewMessage(''); // Clear input immediately for better UX
|
||||
setNewMessage(""); // Clear input immediately for better UX
|
||||
setSelectedImage(null);
|
||||
setImagePreview(null);
|
||||
if (fileInputRef.current) {
|
||||
fileInputRef.current.value = '';
|
||||
fileInputRef.current.value = "";
|
||||
}
|
||||
|
||||
try {
|
||||
// Build FormData for message (with or without image)
|
||||
const formData = new FormData();
|
||||
formData.append('receiverId', recipient.id);
|
||||
formData.append('content', messageContent || ' '); // Send space if only image
|
||||
formData.append("receiverId", recipient.id);
|
||||
formData.append("content", messageContent || " "); // Send space if only image
|
||||
if (imageToSend) {
|
||||
formData.append('image', imageToSend);
|
||||
formData.append("image", imageToSend);
|
||||
}
|
||||
|
||||
const response = await messageAPI.sendMessage(formData);
|
||||
@@ -333,13 +362,13 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
// Socket will handle updating the receiver's chat
|
||||
setMessages((prevMessages) => {
|
||||
// Avoid duplicates
|
||||
if (prevMessages.some(m => m.id === response.data.id)) {
|
||||
if (prevMessages.some((m) => m.id === response.data.id)) {
|
||||
return prevMessages;
|
||||
}
|
||||
return [...prevMessages, response.data];
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to send message:', error);
|
||||
console.error("Failed to send message:", error);
|
||||
setNewMessage(messageContent); // Restore message on error
|
||||
setSelectedImage(imageToSend);
|
||||
if (imageToSend) {
|
||||
@@ -362,10 +391,10 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
|
||||
const formatTime = (dateString: string) => {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleTimeString('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
hour12: true
|
||||
return date.toLocaleTimeString("en-US", {
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
hour12: true,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -374,19 +403,19 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
const today = new Date();
|
||||
|
||||
if (date.toDateString() === today.toDateString()) {
|
||||
return 'Today';
|
||||
return "Today";
|
||||
}
|
||||
|
||||
const yesterday = new Date(today);
|
||||
yesterday.setDate(yesterday.getDate() - 1);
|
||||
if (date.toDateString() === yesterday.toDateString()) {
|
||||
return 'Yesterday';
|
||||
return "Yesterday";
|
||||
}
|
||||
|
||||
return date.toLocaleDateString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: date.getFullYear() !== today.getFullYear() ? 'numeric' : undefined
|
||||
return date.toLocaleDateString("en-US", {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
year: date.getFullYear() !== today.getFullYear() ? "numeric" : undefined,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -396,13 +425,13 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
<div
|
||||
className="position-fixed bottom-0 end-0 m-3 shadow-lg d-flex flex-column"
|
||||
style={{
|
||||
width: '350px',
|
||||
height: '500px',
|
||||
maxHeight: 'calc(100vh - 100px)',
|
||||
width: "350px",
|
||||
height: "500px",
|
||||
maxHeight: "calc(100vh - 100px)",
|
||||
zIndex: 1050,
|
||||
borderRadius: '12px',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: 'white'
|
||||
borderRadius: "12px",
|
||||
overflow: "hidden",
|
||||
backgroundColor: "white",
|
||||
}}
|
||||
>
|
||||
{/* Header */}
|
||||
@@ -413,18 +442,20 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
src={recipient.profileImage}
|
||||
alt={`${recipient.firstName} ${recipient.lastName}`}
|
||||
className="rounded-circle me-2"
|
||||
style={{ width: '35px', height: '35px', objectFit: 'cover' }}
|
||||
style={{ width: "35px", height: "35px", objectFit: "cover" }}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className="rounded-circle bg-white bg-opacity-25 d-flex align-items-center justify-content-center me-2"
|
||||
style={{ width: '35px', height: '35px' }}
|
||||
style={{ width: "35px", height: "35px" }}
|
||||
>
|
||||
<i className="bi bi-person-fill text-white"></i>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<h6 className="mb-0">{recipient.firstName} {recipient.lastName}</h6>
|
||||
<h6 className="mb-0">
|
||||
{recipient.firstName} {recipient.lastName}
|
||||
</h6>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
@@ -440,8 +471,8 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
onScroll={handleScroll}
|
||||
className="p-3 overflow-auto flex-grow-1"
|
||||
style={{
|
||||
backgroundColor: '#f8f9fa',
|
||||
minHeight: 0
|
||||
backgroundColor: "#f8f9fa",
|
||||
minHeight: 0,
|
||||
}}
|
||||
>
|
||||
{loading ? (
|
||||
@@ -452,15 +483,22 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
</div>
|
||||
) : messages.length === 0 ? (
|
||||
<div className="text-center py-5">
|
||||
<i className="bi bi-chat-dots" style={{ fontSize: '3rem', color: '#dee2e6' }}></i>
|
||||
<p className="text-muted mt-2">Start a conversation with {recipient.firstName}</p>
|
||||
<i
|
||||
className="bi bi-chat-dots"
|
||||
style={{ fontSize: "3rem", color: "#dee2e6" }}
|
||||
></i>
|
||||
<p className="text-muted mt-2">
|
||||
Start a conversation with {recipient.firstName}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{messages.map((message, index) => {
|
||||
const isCurrentUser = message.senderId === currentUser?.id;
|
||||
const showDate = index === 0 ||
|
||||
formatDate(message.createdAt) !== formatDate(messages[index - 1].createdAt);
|
||||
const showDate =
|
||||
index === 0 ||
|
||||
formatDate(message.createdAt) !==
|
||||
formatDate(messages[index - 1].createdAt);
|
||||
|
||||
return (
|
||||
<div key={message.id} ref={setMessageRef(message.id)}>
|
||||
@@ -471,16 +509,20 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
</small>
|
||||
</div>
|
||||
)}
|
||||
<div className={`d-flex mb-2 ${isCurrentUser ? 'justify-content-end' : ''}`}>
|
||||
<div
|
||||
className={`d-flex mb-2 ${
|
||||
isCurrentUser ? "justify-content-end" : ""
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`px-3 py-2 rounded-3 ${
|
||||
isCurrentUser
|
||||
? 'bg-primary text-white'
|
||||
: 'bg-white border'
|
||||
? "bg-primary text-white"
|
||||
: "bg-white border"
|
||||
}`}
|
||||
style={{
|
||||
maxWidth: '75%',
|
||||
wordBreak: 'break-word'
|
||||
maxWidth: "75%",
|
||||
wordBreak: "break-word",
|
||||
}}
|
||||
>
|
||||
{message.imagePath && (
|
||||
@@ -489,24 +531,29 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
src={getMessageImageUrl(message.imagePath)}
|
||||
alt="Shared image"
|
||||
style={{
|
||||
width: '100%',
|
||||
borderRadius: '8px',
|
||||
cursor: 'pointer',
|
||||
maxHeight: '300px',
|
||||
objectFit: 'cover'
|
||||
width: "100%",
|
||||
borderRadius: "8px",
|
||||
cursor: "pointer",
|
||||
maxHeight: "300px",
|
||||
objectFit: "cover",
|
||||
}}
|
||||
onClick={() => window.open(getMessageImageUrl(message.imagePath!), '_blank')}
|
||||
onClick={() =>
|
||||
window.open(
|
||||
getMessageImageUrl(message.imagePath!),
|
||||
"_blank"
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{message.content.trim() && (
|
||||
<p className="mb-1" style={{ fontSize: '0.95rem' }}>
|
||||
<p className="mb-1" style={{ fontSize: "0.95rem" }}>
|
||||
{message.content}
|
||||
</p>
|
||||
)}
|
||||
<small
|
||||
className={isCurrentUser ? 'opacity-75' : 'text-muted'}
|
||||
style={{ fontSize: '0.75rem' }}
|
||||
className={isCurrentUser ? "opacity-75" : "text-muted"}
|
||||
style={{ fontSize: "0.75rem" }}
|
||||
>
|
||||
{formatTime(message.createdAt)}
|
||||
</small>
|
||||
@@ -536,17 +583,22 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
src={imagePreview}
|
||||
alt="Preview"
|
||||
style={{
|
||||
maxWidth: '150px',
|
||||
maxHeight: '150px',
|
||||
borderRadius: '8px',
|
||||
objectFit: 'cover'
|
||||
maxWidth: "150px",
|
||||
maxHeight: "150px",
|
||||
borderRadius: "8px",
|
||||
objectFit: "cover",
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-danger position-absolute top-0 end-0 m-1 rounded-circle"
|
||||
onClick={handleRemoveImage}
|
||||
style={{ width: '24px', height: '24px', padding: '0', fontSize: '0.7rem' }}
|
||||
style={{
|
||||
width: "24px",
|
||||
height: "24px",
|
||||
padding: "0",
|
||||
fontSize: "0.7rem",
|
||||
}}
|
||||
>
|
||||
<i className="bi bi-x"></i>
|
||||
</button>
|
||||
@@ -558,7 +610,7 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={handleImageSelect}
|
||||
style={{ display: 'none' }}
|
||||
style={{ display: "none" }}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
@@ -584,7 +636,11 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
disabled={sending || (!newMessage.trim() && !selectedImage)}
|
||||
>
|
||||
{sending ? (
|
||||
<span className="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
<span
|
||||
className="spinner-border spinner-border-sm"
|
||||
role="status"
|
||||
aria-hidden="true"
|
||||
></span>
|
||||
) : (
|
||||
<i className="bi bi-send-fill"></i>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import React, { createContext, useContext, useEffect, useState, useCallback, ReactNode } from 'react';
|
||||
import { Socket } from 'socket.io-client';
|
||||
import socketService from '../services/socket';
|
||||
import React, {
|
||||
createContext,
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
useCallback,
|
||||
ReactNode,
|
||||
} from "react";
|
||||
import { Socket } from "socket.io-client";
|
||||
import socketService from "../services/socket";
|
||||
|
||||
/**
|
||||
* Socket Context Type
|
||||
@@ -14,8 +21,20 @@ interface SocketContextType {
|
||||
emitTypingStop: (receiverId: string) => void;
|
||||
emitMarkMessageRead: (messageId: string, senderId: string) => void;
|
||||
onNewMessage: (callback: (message: any) => void) => () => void;
|
||||
onMessageRead: (callback: (data: { messageId: string; readAt: string; readBy: string }) => void) => () => void;
|
||||
onUserTyping: (callback: (data: { userId: string; firstName: string; isTyping: boolean }) => void) => () => void;
|
||||
onMessageRead: (
|
||||
callback: (data: {
|
||||
messageId: string;
|
||||
readAt: string;
|
||||
readBy: string;
|
||||
}) => void
|
||||
) => () => void;
|
||||
onUserTyping: (
|
||||
callback: (data: {
|
||||
userId: string;
|
||||
firstName: string;
|
||||
isTyping: boolean;
|
||||
}) => void
|
||||
) => () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,7 +56,7 @@ interface SocketProviderProps {
|
||||
*/
|
||||
export const SocketProvider: React.FC<SocketProviderProps> = ({
|
||||
children,
|
||||
isAuthenticated = false
|
||||
isAuthenticated = false,
|
||||
}) => {
|
||||
const [socket, setSocket] = useState<Socket | null>(null);
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
@@ -46,27 +65,20 @@ export const SocketProvider: React.FC<SocketProviderProps> = ({
|
||||
* Initialize socket connection when user is authenticated
|
||||
*/
|
||||
useEffect(() => {
|
||||
console.log('[SocketProvider] useEffect running', { isAuthenticated });
|
||||
|
||||
if (!isAuthenticated) {
|
||||
console.log('[SocketProvider] Not authenticated, skipping socket setup');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[SocketProvider] Initializing socket connection');
|
||||
const newSocket = socketService.connect();
|
||||
setSocket(newSocket);
|
||||
|
||||
// Listen for connection status changes
|
||||
console.log('[SocketProvider] Setting up connection listener');
|
||||
const removeListener = socketService.addConnectionListener((connected) => {
|
||||
console.log('[SocketProvider] Connection status changed:', connected);
|
||||
setIsConnected(connected);
|
||||
});
|
||||
|
||||
// Cleanup on unmount
|
||||
return () => {
|
||||
console.log('[SocketProvider] Cleaning up connection listener');
|
||||
removeListener();
|
||||
};
|
||||
}, [isAuthenticated]);
|
||||
@@ -76,7 +88,6 @@ export const SocketProvider: React.FC<SocketProviderProps> = ({
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (!isAuthenticated && socket) {
|
||||
console.log('[SocketProvider] User logged out, disconnecting socket');
|
||||
socketService.disconnect();
|
||||
setSocket(null);
|
||||
setIsConnected(false);
|
||||
@@ -114,9 +125,12 @@ export const SocketProvider: React.FC<SocketProviderProps> = ({
|
||||
/**
|
||||
* Emit mark message as read event
|
||||
*/
|
||||
const emitMarkMessageRead = useCallback((messageId: string, senderId: string) => {
|
||||
socketService.emitMarkMessageRead(messageId, senderId);
|
||||
}, []);
|
||||
const emitMarkMessageRead = useCallback(
|
||||
(messageId: string, senderId: string) => {
|
||||
socketService.emitMarkMessageRead(messageId, senderId);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
/**
|
||||
* Listen for new messages
|
||||
@@ -128,16 +142,34 @@ export const SocketProvider: React.FC<SocketProviderProps> = ({
|
||||
/**
|
||||
* Listen for message read events
|
||||
*/
|
||||
const onMessageRead = useCallback((callback: (data: { messageId: string; readAt: string; readBy: string }) => void) => {
|
||||
return socketService.onMessageRead(callback);
|
||||
}, []);
|
||||
const onMessageRead = useCallback(
|
||||
(
|
||||
callback: (data: {
|
||||
messageId: string;
|
||||
readAt: string;
|
||||
readBy: string;
|
||||
}) => void
|
||||
) => {
|
||||
return socketService.onMessageRead(callback);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
/**
|
||||
* Listen for typing indicators
|
||||
*/
|
||||
const onUserTyping = useCallback((callback: (data: { userId: string; firstName: string; isTyping: boolean }) => void) => {
|
||||
return socketService.onUserTyping(callback);
|
||||
}, []);
|
||||
const onUserTyping = useCallback(
|
||||
(
|
||||
callback: (data: {
|
||||
userId: string;
|
||||
firstName: string;
|
||||
isTyping: boolean;
|
||||
}) => void
|
||||
) => {
|
||||
return socketService.onUserTyping(callback);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const value: SocketContextType = {
|
||||
socket,
|
||||
@@ -153,9 +185,7 @@ export const SocketProvider: React.FC<SocketProviderProps> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<SocketContext.Provider value={value}>
|
||||
{children}
|
||||
</SocketContext.Provider>
|
||||
<SocketContext.Provider value={value}>{children}</SocketContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -167,7 +197,7 @@ export const useSocket = (): SocketContextType => {
|
||||
const context = useContext(SocketContext);
|
||||
|
||||
if (context === undefined) {
|
||||
throw new Error('useSocket must be used within a SocketProvider');
|
||||
throw new Error("useSocket must be used within a SocketProvider");
|
||||
}
|
||||
|
||||
return context;
|
||||
|
||||
@@ -208,19 +208,11 @@ const ItemDetail: React.FC = () => {
|
||||
const dayTimes = item.weeklyTimes[dayName];
|
||||
availableAfter = dayTimes.availableAfter;
|
||||
availableBefore = dayTimes.availableBefore;
|
||||
console.log("Using day-specific times:", {
|
||||
availableAfter,
|
||||
availableBefore,
|
||||
});
|
||||
}
|
||||
// Otherwise use global times
|
||||
else if (item.availableAfter && item.availableBefore) {
|
||||
availableAfter = item.availableAfter;
|
||||
availableBefore = item.availableBefore;
|
||||
} else {
|
||||
console.log(
|
||||
"No time constraints found, using default 24-hour availability"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +241,6 @@ const ItemDetail: React.FC = () => {
|
||||
|
||||
// If no options are available, return at least one option to prevent empty dropdown
|
||||
if (options.length === 0) {
|
||||
console.log("No valid time options found, showing Not Available");
|
||||
options.push({ value: "00:00", label: "Not Available" });
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Conversation, Message, User } from '../types';
|
||||
import { messageAPI } from '../services/api';
|
||||
import { useAuth } from '../contexts/AuthContext';
|
||||
import { useSocket } from '../contexts/SocketContext';
|
||||
import ChatWindow from '../components/ChatWindow';
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Conversation, 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 { user } = useAuth();
|
||||
@@ -23,17 +23,16 @@ const Messages: React.FC = () => {
|
||||
if (!isConnected) return;
|
||||
|
||||
const cleanup = onNewMessage((newMessage: Message) => {
|
||||
console.log('[Messages] Received new message:', newMessage);
|
||||
|
||||
setConversations((prevConversations) => {
|
||||
// Determine conversation partner
|
||||
const partnerId = newMessage.senderId === user?.id
|
||||
? newMessage.receiverId
|
||||
: newMessage.senderId;
|
||||
const partnerId =
|
||||
newMessage.senderId === user?.id
|
||||
? newMessage.receiverId
|
||||
: newMessage.senderId;
|
||||
|
||||
// Find existing conversation
|
||||
const existingIndex = prevConversations.findIndex(
|
||||
c => c.partnerId === partnerId
|
||||
(c) => c.partnerId === partnerId
|
||||
);
|
||||
|
||||
if (existingIndex !== -1) {
|
||||
@@ -46,7 +45,7 @@ const Messages: React.FC = () => {
|
||||
content: newMessage.content,
|
||||
senderId: newMessage.senderId,
|
||||
createdAt: newMessage.createdAt,
|
||||
isRead: newMessage.isRead
|
||||
isRead: newMessage.isRead,
|
||||
};
|
||||
conv.lastMessageAt = newMessage.createdAt;
|
||||
|
||||
@@ -58,20 +57,21 @@ const Messages: React.FC = () => {
|
||||
updated[existingIndex] = conv;
|
||||
|
||||
// Re-sort by most recent
|
||||
updated.sort((a, b) =>
|
||||
new Date(b.lastMessageAt).getTime() - new Date(a.lastMessageAt).getTime()
|
||||
updated.sort(
|
||||
(a, b) =>
|
||||
new Date(b.lastMessageAt).getTime() -
|
||||
new Date(a.lastMessageAt).getTime()
|
||||
);
|
||||
|
||||
console.log('[Messages] Updated existing conversation');
|
||||
return updated;
|
||||
} else {
|
||||
// New conversation - add to top
|
||||
const partner = newMessage.senderId === user?.id
|
||||
? newMessage.receiver!
|
||||
: newMessage.sender!;
|
||||
const partner =
|
||||
newMessage.senderId === user?.id
|
||||
? newMessage.receiver!
|
||||
: newMessage.sender!;
|
||||
|
||||
if (!partner) {
|
||||
console.warn('[Messages] Partner data missing from new message');
|
||||
return prevConversations;
|
||||
}
|
||||
|
||||
@@ -83,13 +83,13 @@ const Messages: React.FC = () => {
|
||||
content: newMessage.content,
|
||||
senderId: newMessage.senderId,
|
||||
createdAt: newMessage.createdAt,
|
||||
isRead: newMessage.isRead
|
||||
isRead: newMessage.isRead,
|
||||
},
|
||||
unreadCount: newMessage.receiverId === user?.id && !newMessage.isRead ? 1 : 0,
|
||||
lastMessageAt: newMessage.createdAt
|
||||
unreadCount:
|
||||
newMessage.receiverId === user?.id && !newMessage.isRead ? 1 : 0,
|
||||
lastMessageAt: newMessage.createdAt,
|
||||
};
|
||||
|
||||
console.log('[Messages] Created new conversation');
|
||||
return [newConv, ...prevConversations];
|
||||
}
|
||||
});
|
||||
@@ -103,16 +103,17 @@ const Messages: React.FC = () => {
|
||||
if (!isConnected) return;
|
||||
|
||||
const cleanup = onMessageRead((data: any) => {
|
||||
console.log('[Messages] Message read:', data);
|
||||
|
||||
setConversations((prevConversations) => {
|
||||
return prevConversations.map(conv => {
|
||||
return prevConversations.map((conv) => {
|
||||
// If this is the conversation and the last message was marked as read
|
||||
if (conv.lastMessage.id === data.messageId && !conv.lastMessage.isRead) {
|
||||
if (
|
||||
conv.lastMessage.id === data.messageId &&
|
||||
!conv.lastMessage.isRead
|
||||
) {
|
||||
return {
|
||||
...conv,
|
||||
lastMessage: { ...conv.lastMessage, isRead: true },
|
||||
unreadCount: Math.max(0, conv.unreadCount - 1)
|
||||
unreadCount: Math.max(0, conv.unreadCount - 1),
|
||||
};
|
||||
}
|
||||
return conv;
|
||||
@@ -127,10 +128,8 @@ const Messages: React.FC = () => {
|
||||
try {
|
||||
const response = await messageAPI.getConversations();
|
||||
setConversations(response.data);
|
||||
console.log('[Messages] Fetched conversations:', response.data.length);
|
||||
} catch (err: any) {
|
||||
console.error('[Messages] Failed to fetch conversations:', err);
|
||||
setError(err.response?.data?.error || 'Failed to fetch conversations');
|
||||
setError(err.response?.data?.error || "Failed to fetch conversations");
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -142,11 +141,17 @@ const Messages: React.FC = () => {
|
||||
const diffInHours = (now.getTime() - date.getTime()) / (1000 * 60 * 60);
|
||||
|
||||
if (diffInHours < 24) {
|
||||
return date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' });
|
||||
return date.toLocaleTimeString("en-US", {
|
||||
hour: "numeric",
|
||||
minute: "2-digit",
|
||||
});
|
||||
} else if (diffInHours < 48) {
|
||||
return 'Yesterday';
|
||||
return "Yesterday";
|
||||
} else {
|
||||
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
|
||||
return date.toLocaleDateString("en-US", {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -156,14 +161,10 @@ const Messages: React.FC = () => {
|
||||
};
|
||||
|
||||
const handleMessagesRead = (partnerId: string, count: number) => {
|
||||
console.log(`[Messages] ${count} messages marked as read for partner ${partnerId}`);
|
||||
|
||||
// Update the conversation's unread count
|
||||
setConversations(prevConversations =>
|
||||
prevConversations.map(conv =>
|
||||
conv.partnerId === partnerId
|
||||
? { ...conv, unreadCount: 0 }
|
||||
: conv
|
||||
setConversations((prevConversations) =>
|
||||
prevConversations.map((conv) =>
|
||||
conv.partnerId === partnerId ? { ...conv, unreadCount: 0 } : conv
|
||||
)
|
||||
);
|
||||
};
|
||||
@@ -201,23 +202,29 @@ const Messages: React.FC = () => {
|
||||
|
||||
{conversations.length === 0 ? (
|
||||
<div className="text-center py-5">
|
||||
<i className="bi bi-envelope" style={{ fontSize: '3rem', color: '#ccc' }}></i>
|
||||
<i
|
||||
className="bi bi-envelope"
|
||||
style={{ fontSize: "3rem", color: "#ccc" }}
|
||||
></i>
|
||||
<p className="text-muted mt-2">No conversations yet</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="list-group">
|
||||
{conversations.map((conversation) => {
|
||||
const isUnread = conversation.unreadCount > 0;
|
||||
const isLastMessageFromPartner = conversation.lastMessage.senderId === conversation.partnerId;
|
||||
const isLastMessageFromPartner =
|
||||
conversation.lastMessage.senderId === conversation.partnerId;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={conversation.partnerId}
|
||||
className={`list-group-item list-group-item-action ${isUnread ? 'border-start border-primary border-4' : ''}`}
|
||||
className={`list-group-item list-group-item-action ${
|
||||
isUnread ? "border-start border-primary border-4" : ""
|
||||
}`}
|
||||
onClick={() => handleConversationClick(conversation)}
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
backgroundColor: isUnread ? '#f0f7ff' : 'white'
|
||||
cursor: "pointer",
|
||||
backgroundColor: isUnread ? "#f0f7ff" : "white",
|
||||
}}
|
||||
>
|
||||
<div className="d-flex w-100 justify-content-between align-items-start">
|
||||
@@ -228,12 +235,16 @@ const Messages: React.FC = () => {
|
||||
src={conversation.partner.profileImage}
|
||||
alt={`${conversation.partner.firstName} ${conversation.partner.lastName}`}
|
||||
className="rounded-circle me-3"
|
||||
style={{ width: '50px', height: '50px', objectFit: 'cover' }}
|
||||
style={{
|
||||
width: "50px",
|
||||
height: "50px",
|
||||
objectFit: "cover",
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className="rounded-circle bg-secondary d-flex align-items-center justify-content-center me-3"
|
||||
style={{ width: '50px', height: '50px' }}
|
||||
style={{ width: "50px", height: "50px" }}
|
||||
>
|
||||
<i className="bi bi-person-fill text-white"></i>
|
||||
</div>
|
||||
@@ -242,18 +253,25 @@ const Messages: React.FC = () => {
|
||||
<div className="flex-grow-1" style={{ minWidth: 0 }}>
|
||||
{/* User Name and Unread Badge */}
|
||||
<div className="d-flex align-items-center mb-1">
|
||||
<h6 className={`mb-0 ${isUnread ? 'fw-bold' : ''}`}>
|
||||
{conversation.partner.firstName} {conversation.partner.lastName}
|
||||
<h6 className={`mb-0 ${isUnread ? "fw-bold" : ""}`}>
|
||||
{conversation.partner.firstName}{" "}
|
||||
{conversation.partner.lastName}
|
||||
</h6>
|
||||
{isUnread && (
|
||||
<span className="badge bg-primary ms-2">{conversation.unreadCount}</span>
|
||||
<span className="badge bg-primary ms-2">
|
||||
{conversation.unreadCount}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Last Message Preview */}
|
||||
<p
|
||||
className={`mb-0 text-truncate ${isUnread && isLastMessageFromPartner ? 'fw-semibold' : 'text-muted'}`}
|
||||
style={{ maxWidth: '100%' }}
|
||||
className={`mb-0 text-truncate ${
|
||||
isUnread && isLastMessageFromPartner
|
||||
? "fw-semibold"
|
||||
: "text-muted"
|
||||
}`}
|
||||
style={{ maxWidth: "100%" }}
|
||||
>
|
||||
{conversation.lastMessage.senderId === user?.id && (
|
||||
<span className="me-1">You: </span>
|
||||
@@ -264,7 +282,10 @@ const Messages: React.FC = () => {
|
||||
</div>
|
||||
|
||||
{/* Timestamp */}
|
||||
<div className="text-end ms-3" style={{ minWidth: 'fit-content' }}>
|
||||
<div
|
||||
className="text-end ms-3"
|
||||
style={{ minWidth: "fit-content" }}
|
||||
>
|
||||
<small className="text-muted d-block">
|
||||
{formatDate(conversation.lastMessageAt)}
|
||||
</small>
|
||||
|
||||
@@ -52,12 +52,9 @@ class SocketService {
|
||||
*/
|
||||
connect(): Socket {
|
||||
if (this.socket?.connected) {
|
||||
console.log("[Socket] Already connected");
|
||||
return this.socket;
|
||||
}
|
||||
|
||||
console.log("[Socket] Connecting to server...");
|
||||
|
||||
this.socket = io(this.getSocketUrl(), {
|
||||
withCredentials: true, // Send cookies for authentication
|
||||
transports: ["websocket", "polling"], // Try WebSocket first, fallback to polling
|
||||
@@ -71,23 +68,15 @@ class SocketService {
|
||||
|
||||
// Connection event handlers
|
||||
this.socket.on("connect", () => {
|
||||
console.log("[Socket] Connected successfully", {
|
||||
socketId: this.socket?.id,
|
||||
});
|
||||
this.reconnectAttempts = 0;
|
||||
this.notifyConnectionListeners(true);
|
||||
});
|
||||
|
||||
this.socket.on("disconnect", (reason) => {
|
||||
console.log("[Socket] Disconnected", { reason });
|
||||
this.notifyConnectionListeners(false);
|
||||
});
|
||||
|
||||
this.socket.on("connect_error", (error) => {
|
||||
console.error("[Socket] Connection error", {
|
||||
error: error.message,
|
||||
attempt: this.reconnectAttempts + 1,
|
||||
});
|
||||
this.reconnectAttempts++;
|
||||
|
||||
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
||||
@@ -96,10 +85,6 @@ class SocketService {
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.on("error", (error) => {
|
||||
console.error("[Socket] Socket error", error);
|
||||
});
|
||||
|
||||
return this.socket;
|
||||
}
|
||||
|
||||
@@ -108,7 +93,6 @@ class SocketService {
|
||||
*/
|
||||
disconnect(): void {
|
||||
if (this.socket) {
|
||||
console.log("[Socket] Disconnecting...");
|
||||
this.socket.disconnect();
|
||||
this.socket = null;
|
||||
this.notifyConnectionListeners(false);
|
||||
@@ -138,7 +122,6 @@ class SocketService {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("[Socket] Joining conversation", { otherUserId });
|
||||
this.socket.emit("join_conversation", { otherUserId });
|
||||
}
|
||||
|
||||
@@ -150,7 +133,6 @@ class SocketService {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("[Socket] Leaving conversation", { otherUserId });
|
||||
this.socket.emit("leave_conversation", { otherUserId });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user