From 78e85f9e798bdabf829aee6cf662bd9949ba3ca5 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Fri, 22 Nov 2024 07:22:50 +0200 Subject: [PATCH] fix chat list --- src/components/Chat/ChatList.tsx | 495 +++++++++++++++++-------------- 1 file changed, 267 insertions(+), 228 deletions(-) diff --git a/src/components/Chat/ChatList.tsx b/src/components/Chat/ChatList.tsx index 95296d2..fd76b65 100644 --- a/src/components/Chat/ChatList.tsx +++ b/src/components/Chat/ChatList.tsx @@ -1,25 +1,85 @@ -import React, { useCallback, useState, useEffect, useRef, useMemo } from 'react'; -import { useVirtualizer } from '@tanstack/react-virtual'; -import { MessageItem } from './MessageItem'; -import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; -import { useInView } from 'react-intersection-observer' -import { Box } from '@mui/material'; -import { ChatOptions } from './ChatOptions'; +import React, { + useCallback, + useState, + useEffect, + useRef, + useMemo, +} from "react"; +import { useVirtualizer } from "@tanstack/react-virtual"; +import { MessageItem } from "./MessageItem"; +import { subscribeToEvent, unsubscribeFromEvent } from "../../utils/events"; +import { useInView } from "react-intersection-observer"; +import { Box } from "@mui/material"; +import { ChatOptions } from "./ChatOptions"; -export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onReply, handleReaction, chatReferences, tempChatReferences, members, myName, selectedGroup, enableMentions }) => { +export const ChatList = ({ + initialMessages, + myAddress, + tempMessages, + chatId, + onReply, + handleReaction, + chatReferences, + tempChatReferences, + members, + myName, + selectedGroup, + enableMentions, +}) => { const parentRef = useRef(); const [messages, setMessages] = useState(initialMessages); const [showScrollButton, setShowScrollButton] = useState(false); const [showScrollDownButton, setShowScrollDownButton] = useState(false); const hasLoadedInitialRef = useRef(false); + const scrollingIntervalRef = useRef(null); - const showScrollButtonRef = useRef(showScrollButton); + // Initialize the virtualizer + const rowVirtualizer = useVirtualizer({ + count: messages.length, + getItemKey: (index) => messages[index].signature, + getScrollElement: () => parentRef?.current, + estimateSize: () => 80, // Provide an estimated height of items, adjust this as needed + overscan: 10, // Number of items to render outside the visible area to improve smoothness + }); -// Update the ref whenever the state changes -useEffect(() => { - showScrollButtonRef.current = showScrollButton; -}, [showScrollButton]); + const isAtBottom = useMemo(()=> { + if (parentRef.current && rowVirtualizer?.isScrolling !== undefined) { + const { scrollTop, scrollHeight, clientHeight } = parentRef.current; + const atBottom = scrollTop + clientHeight >= scrollHeight - 10; // Adjust threshold as needed + return atBottom + } + + return false + + }, [rowVirtualizer?.isScrolling]) + + useEffect(() => { + if (!parentRef.current || rowVirtualizer?.isScrolling === undefined) return; + if(isAtBottom){ + if (scrollingIntervalRef.current) { + clearTimeout(scrollingIntervalRef.current); + } + setShowScrollDownButton(false); + return; + } else + if (rowVirtualizer?.isScrolling) { + if (scrollingIntervalRef.current) { + clearTimeout(scrollingIntervalRef.current); + } + setShowScrollDownButton(false); + return; + } + const { scrollTop, scrollHeight, clientHeight } = parentRef.current; + const atBottom = scrollHeight - scrollTop - clientHeight <= 300; + if (!atBottom) { + scrollingIntervalRef.current = setTimeout(() => { + setShowScrollDownButton(true); + }, 250); + } else { + setShowScrollDownButton(false); + } + }, [rowVirtualizer?.isScrolling, isAtBottom]); // Update message list with unique signatures and tempMessages useEffect(() => { @@ -32,30 +92,35 @@ useEffect(() => { } }); - const uniqueInitialMessages = Array.from(uniqueInitialMessagesMap.values()).sort( - (a, b) => a.timestamp - b.timestamp - ); - const totalMessages = [...uniqueInitialMessages, ...(tempMessages || [])] + const uniqueInitialMessages = Array.from( + uniqueInitialMessagesMap.values() + ).sort((a, b) => a.timestamp - b.timestamp); + const totalMessages = [...uniqueInitialMessages, ...(tempMessages || [])]; if (totalMessages.length === 0) return; setMessages(totalMessages); setTimeout(() => { - const hasUnreadMessages = totalMessages.some((msg) => msg.unread && !msg?.chatReference && !msg?.isTemp); + const hasUnreadMessages = totalMessages.some( + (msg) => msg.unread && !msg?.chatReference && !msg?.isTemp + ); if (parentRef.current) { const { scrollTop, scrollHeight, clientHeight } = parentRef.current; - const atBottom = scrollTop + clientHeight >= scrollHeight - 10; // Adjust threshold as needed + const atBottom = scrollTop + clientHeight >= scrollHeight - 10; // Adjust threshold as needed if (!atBottom && hasUnreadMessages) { setShowScrollButton(hasUnreadMessages); - setShowScrollDownButton(false) + setShowScrollDownButton(false); } else { handleMessageSeen(); } } if (!hasLoadedInitialRef.current) { - const findDivideIndex = totalMessages.findIndex((item)=> !!item?.divide) - const divideIndex = findDivideIndex !== -1 ? findDivideIndex : undefined + const findDivideIndex = totalMessages.findIndex( + (item) => !!item?.divide + ); + const divideIndex = + findDivideIndex !== -1 ? findDivideIndex : undefined; scrollToBottom(totalMessages, divideIndex); hasLoadedInitialRef.current = true; } @@ -65,19 +130,15 @@ useEffect(() => { const scrollToBottom = (initialMsgs, divideIndex) => { const index = initialMsgs ? initialMsgs.length - 1 : messages.length - 1; if (rowVirtualizer) { - if(divideIndex){ - rowVirtualizer.scrollToIndex(divideIndex, { align: 'start' }) + if (divideIndex) { + rowVirtualizer.scrollToIndex(divideIndex, { align: "start" }); } else { - rowVirtualizer.scrollToIndex(index, { align: 'end' }) - + rowVirtualizer.scrollToIndex(index, { align: "end" }); } } - handleMessageSeen() + handleMessageSeen(); }; - - - const handleMessageSeen = useCallback(() => { setMessages((prevMessages) => prevMessages.map((msg) => ({ @@ -85,236 +146,214 @@ useEffect(() => { unread: false, })) ); - setShowScrollButton(false) + setShowScrollButton(false); }, []); - - const sentNewMessageGroupFunc = useCallback(() => { const { scrollHeight, scrollTop, clientHeight } = parentRef.current; - + // Check if the user is within 200px from the bottom const distanceFromBottom = scrollHeight - scrollTop - clientHeight; - console.log('distanceFromBottom', distanceFromBottom) + console.log("distanceFromBottom", distanceFromBottom); if (distanceFromBottom <= 700) { scrollToBottom(); } }, [messages]); - useEffect(() => { - subscribeToEvent('sent-new-message-group', sentNewMessageGroupFunc); + subscribeToEvent("sent-new-message-group", sentNewMessageGroupFunc); return () => { - unsubscribeFromEvent('sent-new-message-group', sentNewMessageGroupFunc); + unsubscribeFromEvent("sent-new-message-group", sentNewMessageGroupFunc); }; }, [sentNewMessageGroupFunc]); - const lastSignature = useMemo(()=> { - if(!messages || messages?.length === 0) return null - const lastIndex = messages.length - 1 - return messages[lastIndex]?.signature - }, [messages]) + const lastSignature = useMemo(() => { + if (!messages || messages?.length === 0) return null; + const lastIndex = messages.length - 1; + return messages[lastIndex]?.signature; + }, [messages]); - - // Initialize the virtualizer - const rowVirtualizer = useVirtualizer({ - count: messages.length, - getItemKey: React.useCallback( - (index) => messages[index].signature, - [messages] - ), - getScrollElement: () => parentRef.current, - estimateSize: () => 80, // Provide an estimated height of items, adjust this as needed - overscan: 10, // Number of items to render outside the visible area to improve smoothness - observeElementOffset: (instance, cb) => { - const offsetCheck = () => { - const { scrollHeight, scrollTop, clientHeight } = instance.scrollElement; - const atBottom = scrollHeight - scrollTop - clientHeight <= 300; - if(showScrollButtonRef.current){ - setShowScrollDownButton(false) - } else - if(atBottom){ - setShowScrollDownButton(false) - - } else { - setShowScrollDownButton(true) - - } - cb(scrollTop); // Pass scroll offset to callback - // setShowScrollToBottom(!atBottom); - }; - - // Initial check and continuous monitoring - offsetCheck(); - instance.scrollElement.addEventListener('scroll', offsetCheck); - return () => instance.scrollElement.removeEventListener('scroll', offsetCheck); - }, - - }); - - const goToMessage = useCallback((idx)=> { - rowVirtualizer.scrollToIndex(idx) - }, []) - - - + const goToMessage = useCallback((idx) => { + rowVirtualizer.scrollToIndex(idx); + }, []); return ( - -
- -
+
-
- {rowVirtualizer.getVirtualItems().map((virtualRow) => { - const index = virtualRow.index; - let message = messages[index]; - let replyIndex = messages.findIndex((msg) => msg?.signature === message?.repliedTo); - let reply; - let reactions = null; - - if (message?.repliedTo && replyIndex !== -1) { - reply = messages[replyIndex]; - } - - if (message?.message && message?.groupDirectId) { - replyIndex = messages.findIndex((msg) => msg?.signature === message?.message?.repliedTo); - if (message?.message?.repliedTo && replyIndex !== -1) { - reply = messages[replyIndex]; - } - message = { - ...(message?.message || {}), - isTemp: true, - unread: false, - status: message?.status - }; - } - - if (chatReferences && chatReferences[message?.signature]) { - if (chatReferences[message.signature]?.reactions) { - reactions = chatReferences[message.signature]?.reactions; - } - } - - let isUpdating = false; - if (tempChatReferences && tempChatReferences?.find((item) => item?.chatReference === message?.signature)) { - isUpdating = true; - } - - return (
- -
- ); - })} -
-
- -
- {showScrollButton && ( - - )} - {showScrollDownButton && ( - - )} -
- {enableMentions && ( - + {rowVirtualizer.getVirtualItems().map((virtualRow) => { + const index = virtualRow.index; + let message = messages[index]; + let replyIndex = messages.findIndex( + (msg) => msg?.signature === message?.repliedTo + ); + let reply; + let reactions = null; - )} - + if (message?.repliedTo && replyIndex !== -1) { + reply = messages[replyIndex]; + } + + if (message?.message && message?.groupDirectId) { + replyIndex = messages.findIndex( + (msg) => msg?.signature === message?.message?.repliedTo + ); + if (message?.message?.repliedTo && replyIndex !== -1) { + reply = messages[replyIndex]; + } + message = { + ...(message?.message || {}), + isTemp: true, + unread: false, + status: message?.status, + }; + } + + if (chatReferences && chatReferences[message?.signature]) { + if (chatReferences[message.signature]?.reactions) { + reactions = chatReferences[message.signature]?.reactions; + } + } + + let isUpdating = false; + if ( + tempChatReferences && + tempChatReferences?.find( + (item) => item?.chatReference === message?.signature + ) + ) { + isUpdating = true; + } + + return ( +
+ +
+ ); + })} +
+ + + {showScrollButton && ( + + )} + {showScrollDownButton && ( + + )} + + {enableMentions && ( + + )} +
); };