Closed Dun-sin closed 3 months ago
The issue has been unlocked and is now ready for dev. If you would like to work on this issue, you can comment to have it assigned to you.
Could I take on this issue? I would solve it by creating a new file "useObserver.js" in the folder "hooks":
import useChatUtils from 'src/lib/chatSocket';
import { socket } from 'src/lib/socketConnection';
import { useChat } from 'src/context/ChatContext';
import { useApp } from 'src/context/AppContext';
export default (isSender) => {
const { seenMessage } = useChatUtils(socket);
const { receiveMessage } = useChat();
const { app } = useApp();
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting && !isSender) {
// Mark the message as read
const messageId = entry.target.getAttribute('id').split('-')[1];
try {
seenMessage({
messageId,
chatId: app.currentChatId,
});
} catch (e) {
return;
}
receiveMessage(messageId, app.currentChatId);
}
});
},
{ threshold: 0.5 }
// Trigger when 50% of the element is in the viewport
);
return observer
};
And I would make adjustments to "MessageSeen.jsx" component:
import React, { useEffect, useMemo } from 'react';
import PropTypes from 'prop-types';
import useIsTabActive from 'src/hooks/useIsTabActive';
import useObserver from 'src/hooks/useObserver';
import { useApp } from 'src/context/AppContext';
import { useChat } from 'src/context/ChatContext';
const MessageSeen = ({ isRead, isSender }) => {
const { app } = useApp();
const isTabVisible = useIsTabActive();
const observer = useObserver(isSender);
const { messages: state } = useChat();
const sortedMessages = useMemo(
() =>
Object.values(state[app.currentChatId]?.messages ?? {})?.sort((a, b) => {
const da = new Date(a.time),
db = new Date(b.time);
return da - db;
}),
[state, app.currentChatId]
);
useEffect(() => {
// Initialize Intersection Observer
observer.connect();
sortedMessages.forEach((message) => {
if (message.isRead) {
return;
}
const messageElement = document.getElementById(`message-${message.id}`);
if (messageElement && isTabVisible) {
observer.observe(messageElement);
}
});
return () => {
// Clean up the observer
observer.disconnect();
};
}, [sortedMessages, isTabVisible]);
return isSender && <p className="text-sm">{isRead ? 'Seen' : 'Not Seen'}</p>;
};
export default MessageSeen;
MessageSeen.propTypes = {
isRead: PropTypes.bool,
isSender: PropTypes.bool.isRequired,
};
If you want me to, I could memoize the observer hook to prevent unnecessary re-creations whenever component renders.
Could I take on this issue? I would solve it by creating a new file "useObserver.js" in the folder "hooks":
import useChatUtils from 'src/lib/chatSocket'; import { socket } from 'src/lib/socketConnection'; import { useChat } from 'src/context/ChatContext'; import { useApp } from 'src/context/AppContext'; export default (isSender) => { const { seenMessage } = useChatUtils(socket); const { receiveMessage } = useChat(); const { app } = useApp(); const observer = new IntersectionObserver( (entries) => { entries.forEach((entry) => { if (entry.isIntersecting && !isSender) { // Mark the message as read const messageId = entry.target.getAttribute('id').split('-')[1]; try { seenMessage({ messageId, chatId: app.currentChatId, }); } catch (e) { return; } receiveMessage(messageId, app.currentChatId); } }); }, { threshold: 0.5 } // Trigger when 50% of the element is in the viewport ); return observer };
And I would make adjustments to "MessageSeen.jsx" component:
import React, { useEffect, useMemo } from 'react'; import PropTypes from 'prop-types'; import useIsTabActive from 'src/hooks/useIsTabActive'; import useObserver from 'src/hooks/useObserver'; import { useApp } from 'src/context/AppContext'; import { useChat } from 'src/context/ChatContext'; const MessageSeen = ({ isRead, isSender }) => { const { app } = useApp(); const isTabVisible = useIsTabActive(); const observer = useObserver(isSender); const { messages: state } = useChat(); const sortedMessages = useMemo( () => Object.values(state[app.currentChatId]?.messages ?? {})?.sort((a, b) => { const da = new Date(a.time), db = new Date(b.time); return da - db; }), [state, app.currentChatId] ); useEffect(() => { // Initialize Intersection Observer observer.connect(); sortedMessages.forEach((message) => { if (message.isRead) { return; } const messageElement = document.getElementById(`message-${message.id}`); if (messageElement && isTabVisible) { observer.observe(messageElement); } }); return () => { // Clean up the observer observer.disconnect(); }; }, [sortedMessages, isTabVisible]); return isSender && <p className="text-sm">{isRead ? 'Seen' : 'Not Seen'}</p>; }; export default MessageSeen; MessageSeen.propTypes = { isRead: PropTypes.bool, isSender: PropTypes.bool.isRequired, };
If you want me to, I could memoize the observer hook to prevent unnecessary re-creations whenever component renders.
Sure, go for it💪🏽
What would you like to share?
https://github.com/Dun-sin/Whisper/blob/dbec99c68f63c606647ea2cef1d458a72ae83b1b/client/src/components/Chat/MessageSeen.jsx#L29-L68