Edit Del Not Working Codee)
Edit Del Not Working Codee)
Edit Del Not Working Codee)
import {
Chat as ChatIcon,
Send as SendIcon,
Check as CheckIcon,
DoneAll as DoneAllIcon,
Search as SearchIcon,
Close as CloseIcon,
ArrowUpward as ArrowUpwardIcon,
ArrowDownward as ArrowDownwardIcon,
Edit as EditIcon,
Delete as DeleteIcon,
} from '@mui/icons-material';
import io from 'socket.io-client';
try {
const response = await fetch(`${BACKEND_API}api/messages/$
{selectedMessage._id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ text: editedMessage.trim() }),
});
if (response.ok) {
const updatedMessage = await response.json();
setMessages(prevMessages =>
prevMessages.map(msg =>
msg._id === selectedMessage._id
? {
...msg,
text: updatedMessage.text,
isEdited: true,
editedAt: updatedMessage.editedAt
}
: msg
)
);
socketRef.current.emit('messageEdited', updatedMessage);
}
} catch (error) {
console.error('Error editing message:', error);
}
setIsEditing(false);
setSelectedMessage(null);
};
if (response.ok) {
setMessages(prevMessages =>
prevMessages.filter(msg => msg._id !== selectedMessage._id)
);
socketRef.current.emit('messageDeleted', selectedMessage._id);
}
} catch (error) {
console.error('Error deleting message:', error);
}
setDeleteConfirmOpen(false);
setSelectedMessage(null);
};
setSearchResults(results);
setCurrentResultIndex(results.length > 0 ? 0 : -1);
if (results.length > 0) {
scrollToMessage(results[0]);
}
};
let newIndex;
if (direction === 'up') {
newIndex = currentResultIndex > 0 ? currentResultIndex - 1 :
searchResults.length - 1;
} else {
newIndex = currentResultIndex < searchResults.length - 1 ? currentResultIndex +
1 : 0;
}
setCurrentResultIndex(newIndex);
scrollToMessage(searchResults[newIndex]);
};
useEffect(() => {
scrollToBottom('auto');
}, [messages]);
return date.toLocaleString([], {
hour: '2-digit',
minute: '2-digit',
hour12: true
});
} catch (error) {
console.error('Error formatting date:', error);
return '';
}
};
if (messageData &&
messageData.sender === receiverId &&
messageData.status !== 'read') {
handleMessageRead(messageId);
}
}
});
},
{ threshold: 0.5 }
);
return () => {
messageElements.forEach((element) => observer.unobserve(element));
};
}, [messages, receiverId]);
if (unreadMessages.length > 0) {
const messageIds = unreadMessages.map(msg => msg._id);
socketRef.current.emit('messageRead', {
messageIds,
sender: receiverId
});
}
}
};
useEffect(() => {
if (messageContainerRef.current) {
const { scrollHeight, scrollTop, clientHeight } =
messageContainerRef.current;
const isAtBottom = Math.abs(scrollHeight - scrollTop - clientHeight) < 50;
if (isAtBottom) {
scrollToBottom();
}
}
}, [messages]);
useEffect(() => {
if (senderId && receiverId) {
if (socketRef.current) {
socketRef.current.disconnect();
}
socketRef.current = io(socketURL, {
withCredentials: true,
transports: ['websocket', 'polling'],
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 1000,
timeout: 20000,
autoConnect: true
});
socketRef.current.on('connect', () => {
setSocketConnected(true);
setConnectionError(null);
socketRef.current.emit('register', senderId);
});
socketRef.current.on('receiveMessage', handleReceiveMessage);
socketRef.current.on('messageDelivered', handleMessageDelivered);
socketRef.current.on('messagesRead', handleMessagesRead);
socketRef.current.on('messagesReadConfirmation',
handleMessagesReadConfirmation);
fetch(`${BACKEND_API}api/messages/${bidId}/${senderId}/${receiverId}`)
.then(response => response.json())
.then(data => {
const formattedData = data.map(msg => ({
...msg,
timestamp: msg.timestamp ? new Date(msg.timestamp).toISOString() : new
Date().toISOString(),
readAt: msg.readAt ? new Date(msg.readAt).toISOString() : null,
editedAt: msg.editedAt ? new Date(msg.editedAt).toISOString() : null,
isEdited: msg.isEdited || false, // Ensure isEdited is preserved
}));
setMessages(formattedData);
setTimeout(scrollToBottom, 100);
})
.catch(err => console.error('Error fetching messages:', err));
return () => {
if (socketRef.current) {
socketRef.current.off('receiveMessage', handleReceiveMessage);
socketRef.current.off('messageDelivered', handleMessageDelivered);
socketRef.current.off('messagesRead', handleMessagesRead);
socketRef.current.off('messagesReadConfirmation',
handleMessagesReadConfirmation);
socketRef.current.disconnect();
socketRef.current?.off('messageUpdated');
}
};
}
},
[senderId, receiverId, bidId, BACKEND_API]);
fetch(`${BACKEND_API}api/messages`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newMessage),
})
.then(response => response.json())
.then(savedMessage => {
socketRef.current.emit('sendMessage', savedMessage);
setMessages(prevMessages => {
const messageExists = prevMessages.some(msg =>
msg.timestamp === savedMessage.timestamp &&
msg.sender === savedMessage.sender &&
msg.text === savedMessage.text
);
return (
<Card sx={{ width: '100%', maxWidth: 750, mx: 'auto' }}>
<CardHeader
sx={{
bgcolor: 'primary.main',
color: 'primary.contrastText',
p: 2,
}}
title={
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems:
'center' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<ChatIcon fontSize="small" />
<Typography variant="h6">Chat with {role}</Typography>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<IconButton
size="small"
onClick={handleSearchIconClick}
sx={{ color: 'inherit' }}
>
<SearchIcon />
</IconButton>
<Typography variant="body2" sx={{ opacity: 0.9 }}>
{socketConnected ? 'Connected' : 'Disconnected'}
</Typography>
</Box>
</Box>
}
/>
<Collapse in={showSearch}>
<Box sx={{ p: 2, bgcolor: 'grey.100' }}>
<TextField
fullWidth
size="small"
placeholder="Search messages..."
value={searchText}
onChange={(e) => handleSearch(e.target.value)}
inputRef={textFieldRef}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon fontSize="small" />
</InputAdornment>
),
endAdornment: (
<InputAdornment position="end">
{searchResults.length > 0 && (
<>
<Typography variant="caption" sx={{ mr: 1 }}>
{currentResultIndex + 1} of {searchResults.length}
</Typography>
<IconButton size="small" onClick={() =>
navigateSearch('up')}>
<ArrowUpwardIcon fontSize="small" />
</IconButton>
<IconButton size="small" onClick={() =>
navigateSearch('down')}>
<ArrowDownwardIcon fontSize="small" />
</IconButton>
</>
)}
{searchText && (
<IconButton
size="small"
onClick={() => {
setSearchText('');
setSearchResults([]);
setCurrentResultIndex(-1);
setShowSearch(false);
}}
>
<CloseIcon fontSize="small" />
</IconButton>
)}
</InputAdornment>
),
}}
/>
</Box>
</Collapse>
{connectionError && (
<Alert severity="error" sx={{ m: 1 }}>
{connectionError}
</Alert>
)}
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={handleMenuClose}
anchorOrigin={{
vertical: 'center', // Center vertically with the message
horizontal: 'left', // Position to the left of the message
}}
transformOrigin={{
vertical: 'center',
horizontal: 'right', // Align menu's right edge with message
}}
PaperProps={{
sx: {
// Optional: Add a slight offset from the message
ml: -2, // Move menu slightly away from the message
}
}}
container={messageContainerRef.current}
>
<MenuItem onClick={handleEditClick}>
<EditIcon fontSize="small" sx={{ mr: 1 }} />
Edit
</MenuItem>
<MenuItem onClick={handleDeleteClick}>
<DeleteIcon fontSize="small" sx={{ mr: 1 }} />
Delete
</MenuItem>
</Menu>
<Dialog
open={deleteConfirmOpen}
onClose={() => setDeleteConfirmOpen(false)}
>
<DialogTitle>Delete Message</DialogTitle>
<DialogContent>
Are you sure you want to delete this message?
</DialogContent>
<DialogActions>
<Button onClick={() => setDeleteConfirmOpen(false)}>Cancel</Button>
<Button onClick={handleDeleteConfirm} color="error">
Delete
</Button>
</DialogActions>
</Dialog>
</Card>
);
};