removed console logs from frontend and a logs from locationService
This commit is contained in:
@@ -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,47 +391,47 @@ 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,
|
||||
});
|
||||
};
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
const date = new Date(dateString);
|
||||
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,
|
||||
});
|
||||
};
|
||||
|
||||
if (!show) return null;
|
||||
|
||||
return (
|
||||
<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)',
|
||||
<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)",
|
||||
zIndex: 1050,
|
||||
borderRadius: '12px',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: 'white'
|
||||
borderRadius: "12px",
|
||||
overflow: "hidden",
|
||||
backgroundColor: "white",
|
||||
}}
|
||||
>
|
||||
{/* Header */}
|
||||
@@ -413,23 +442,25 @@ 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
|
||||
<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
|
||||
type="button"
|
||||
className="btn-close btn-close-white"
|
||||
<button
|
||||
type="button"
|
||||
className="btn-close btn-close-white"
|
||||
onClick={onClose}
|
||||
></button>
|
||||
</div>
|
||||
@@ -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>
|
||||
)}
|
||||
@@ -595,4 +651,4 @@ const ChatWindow: React.FC<ChatWindowProps> = ({ show, onClose, recipient, onMes
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatWindow;
|
||||
export default ChatWindow;
|
||||
|
||||
Reference in New Issue
Block a user