improve chat load

This commit is contained in:
PhilReact 2024-11-14 10:22:46 +02:00
parent c405003ed9
commit cf2b78fc5d
2 changed files with 168 additions and 131 deletions

View File

@ -376,6 +376,7 @@ const clearEditorContent = () => {
const onReply = useCallback((message)=> { const onReply = useCallback((message)=> {
setReplyMessage(message) setReplyMessage(message)
editorRef?.current?.chain().focus()
}, []) }, [])

View File

@ -1,10 +1,25 @@
import React, { useCallback, useState, useEffect, useRef, useMemo } from 'react'; import React, {
import { useVirtualizer } from '@tanstack/react-virtual'; useCallback,
import { MessageItem } from './MessageItem'; useState,
import { subscribeToEvent, unsubscribeFromEvent } from '../../utils/events'; useEffect,
import { useInView } from 'react-intersection-observer' 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";
export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onReply, handleReaction, chatReferences, tempChatReferences }) => { export const ChatList = ({
initialMessages,
myAddress,
tempMessages,
chatId,
onReply,
handleReaction,
chatReferences,
tempChatReferences,
}) => {
const parentRef = useRef(); const parentRef = useRef();
const [messages, setMessages] = useState(initialMessages); const [messages, setMessages] = useState(initialMessages);
const [showScrollButton, setShowScrollButton] = useState(false); const [showScrollButton, setShowScrollButton] = useState(false);
@ -16,7 +31,7 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onR
// useEffect(() => { // useEffect(() => {
// if (inView) { // if (inView) {
// } // }
// }, [inView]) // }, [inView])
// Update message list with unique signatures and tempMessages // Update message list with unique signatures and tempMessages
@ -30,9 +45,9 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onR
} }
}); });
const uniqueInitialMessages = Array.from(uniqueInitialMessagesMap.values()).sort( const uniqueInitialMessages = Array.from(
(a, b) => a.timestamp - b.timestamp uniqueInitialMessagesMap.values()
); ).sort((a, b) => a.timestamp - b.timestamp);
const totalMessages = [...uniqueInitialMessages, ...(tempMessages || [])]; const totalMessages = [...uniqueInitialMessages, ...(tempMessages || [])];
if (totalMessages.length === 0) return; if (totalMessages.length === 0) return;
@ -40,10 +55,12 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onR
setMessages(totalMessages); setMessages(totalMessages);
setTimeout(() => { setTimeout(() => {
const hasUnreadMessages = totalMessages.some((msg) => msg.unread && !msg?.chatReference); const hasUnreadMessages = totalMessages.some(
(msg) => msg.unread && !msg?.chatReference
);
if (parentRef.current) { if (parentRef.current) {
const { scrollTop, scrollHeight, clientHeight } = 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) { if (!atBottom && hasUnreadMessages) {
setShowScrollButton(hasUnreadMessages); setShowScrollButton(hasUnreadMessages);
} else { } else {
@ -60,12 +77,11 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onR
const scrollToBottom = (initialMsgs) => { const scrollToBottom = (initialMsgs) => {
const index = initialMsgs ? initialMsgs.length - 1 : messages.length - 1; const index = initialMsgs ? initialMsgs.length - 1 : messages.length - 1;
if (rowVirtualizer) { if (rowVirtualizer) {
rowVirtualizer.scrollToIndex(index, { align: 'end' }); rowVirtualizer.scrollToIndex(index, { align: "end" });
} }
handleMessageSeen() handleMessageSeen();
}; };
const handleMessageSeen = useCallback(() => { const handleMessageSeen = useCallback(() => {
setMessages((prevMessages) => setMessages((prevMessages) =>
prevMessages.map((msg) => ({ prevMessages.map((msg) => ({
@ -73,7 +89,7 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onR
unread: false, unread: false,
})) }))
); );
setShowScrollButton(false) setShowScrollButton(false);
}, []); }, []);
// const scrollToBottom = (initialMsgs) => { // const scrollToBottom = (initialMsgs) => {
@ -83,24 +99,22 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onR
// } // }
// }; // };
const sentNewMessageGroupFunc = useCallback(() => { const sentNewMessageGroupFunc = useCallback(() => {
scrollToBottom(); scrollToBottom();
}, [messages]); }, [messages]);
useEffect(() => { useEffect(() => {
subscribeToEvent('sent-new-message-group', sentNewMessageGroupFunc); subscribeToEvent("sent-new-message-group", sentNewMessageGroupFunc);
return () => { return () => {
unsubscribeFromEvent('sent-new-message-group', sentNewMessageGroupFunc); unsubscribeFromEvent("sent-new-message-group", sentNewMessageGroupFunc);
}; };
}, [sentNewMessageGroupFunc]); }, [sentNewMessageGroupFunc]);
const lastSignature = useMemo(()=> { const lastSignature = useMemo(() => {
if(!messages || messages?.length === 0) return null if (!messages || messages?.length === 0) return null;
const lastIndex = messages.length - 1 const lastIndex = messages.length - 1;
return messages[lastIndex]?.signature return messages[lastIndex]?.signature;
}, [messages]) }, [messages]);
// Initialize the virtualizer // Initialize the virtualizer
const rowVirtualizer = useVirtualizer({ const rowVirtualizer = useVirtualizer({
@ -108,125 +122,147 @@ export const ChatList = ({ initialMessages, myAddress, tempMessages, chatId, onR
getScrollElement: () => parentRef.current, getScrollElement: () => parentRef.current,
estimateSize: () => 80, // Provide an estimated height of items, adjust this as needed 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 overscan: 10, // Number of items to render outside the visible area to improve smoothness
measureElement: getItemKey: React.useCallback(
typeof window !== 'undefined' && (index) => messages[index].signature,
navigator.userAgent.indexOf('Firefox') === -1 [messages]
? element => { ),
return element?.getBoundingClientRect().height
}
: undefined,
}); });
return ( return (
<div style={{ <div
height: '100%', style={{
position: 'relative' height: "100%",
}}> position: "relative",
<div ref={parentRef} style={{ height: '100%', overflow: 'auto', position: 'relative', display: 'flex' }}> display: "flex",
flexDirection: "column",
}}
>
<div <div
ref={parentRef}
className="List"
style={{ style={{
width: '100%', flexGrow: 1,
position: 'relative', overflow: "auto",
display: 'flex', position: "relative",
flexDirection: 'column', display: "flex",
alignItems: 'center', // Center items horizontally height: "0px",
gap: '10px', // Add gap between items
flexGrow: 1
}} }}
> >
{rowVirtualizer.getVirtualItems().map((virtualRow) => { <div
const index = virtualRow.index; style={{
let message = messages[index]; height: rowVirtualizer.getTotalSize(),
let replyIndex = messages.findIndex((msg) => msg?.signature === message?.repliedTo); width: "100%",
let reply; }}
let reactions = null; >
<div
style={{
position: "absolute",
top: 0,
left: 0,
width: "100%",
// transform: `translateY(${rowVirtualizer.getVirtualItems()[0]?.start ?? 0}px)`,
}}
>
{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) { if (message?.repliedTo && replyIndex !== -1) {
reply = messages[replyIndex]; reply = messages[replyIndex];
} }
if (message?.message && message?.groupDirectId) { if (message?.message && message?.groupDirectId) {
replyIndex = messages.findIndex((msg) => msg?.signature === message?.message?.repliedTo); replyIndex = messages.findIndex(
if (message?.message?.repliedTo && replyIndex !== -1) { (msg) => msg?.signature === message?.message?.repliedTo
reply = messages[replyIndex]; );
} if (message?.message?.repliedTo && replyIndex !== -1) {
message = { reply = messages[replyIndex];
...(message?.message || {}), }
isTemp: true, message = {
unread: false, ...(message?.message || {}),
status: message?.status isTemp: true,
}; unread: false,
} status: message?.status,
};
}
if (chatReferences && chatReferences[message?.signature]) { if (chatReferences && chatReferences[message?.signature]) {
if (chatReferences[message.signature]?.reactions) { if (chatReferences[message.signature]?.reactions) {
reactions = chatReferences[message.signature]?.reactions; reactions = chatReferences[message.signature]?.reactions;
} }
} }
let isUpdating = false; let isUpdating = false;
if (tempChatReferences && tempChatReferences?.find((item) => item?.chatReference === message?.signature)) { if (
isUpdating = true; tempChatReferences &&
} tempChatReferences?.find(
(item) => item?.chatReference === message?.signature
)
) {
isUpdating = true;
}
return ( return (
<div <div
data-index={virtualRow.index} //needed for dynamic row height measurement data-index={virtualRow.index} //needed for dynamic row height measurement
ref={node => rowVirtualizer.measureElement(node)} //measure dynamic row height ref={(node) => rowVirtualizer.measureElement(node)} //measure dynamic row height
key={message.signature} key={message.signature}
style={{ style={{
position: 'absolute', position: "absolute",
top: 0, top: 0,
left: '50%', // Move to the center horizontally left: "50%", // Move to the center horizontally
transform: `translateY(${virtualRow.start}px) translateX(-50%)`, // Adjust for centering transform: `translateY(${virtualRow.start}px) translateX(-50%)`, // Adjust for centering
width: '100%', // Control width (90% of the parent) width: "100%", // Control width (90% of the parent)
padding: '10px 0', padding: "10px 0",
display: 'flex', display: "flex",
justifyContent: 'center', justifyContent: "center",
overscrollBehavior: 'none', overscrollBehavior: "none",
}} }}
> >
<MessageItem <MessageItem
isLast={index === messages.length - 1} isLast={index === messages.length - 1}
lastSignature={lastSignature} lastSignature={lastSignature}
message={message} message={message}
onSeen={handleMessageSeen} onSeen={handleMessageSeen}
isTemp={!!message?.isTemp} isTemp={!!message?.isTemp}
myAddress={myAddress} myAddress={myAddress}
onReply={onReply} onReply={onReply}
reply={reply} reply={reply}
replyIndex={replyIndex} replyIndex={replyIndex}
scrollToItem={(idx) => rowVirtualizer.scrollToIndex(idx)} scrollToItem={(idx) => rowVirtualizer.scrollToIndex(idx)}
handleReaction={handleReaction} handleReaction={handleReaction}
reactions={reactions} reactions={reactions}
isUpdating={isUpdating} isUpdating={isUpdating}
/> />
</div> </div>
); );
})} })}
</div>
</div>
</div> </div>
{showScrollButton && (
<button
onClick={() => scrollToBottom()}
style={{
position: "absolute",
bottom: 20,
right: 20,
backgroundColor: "#ff5a5f",
color: "white",
padding: "10px 20px",
borderRadius: "20px",
cursor: "pointer",
zIndex: 10,
}}
>
Scroll to Unread Messages
</button>
)}
</div> </div>
{showScrollButton && (
<button
onClick={() => scrollToBottom()}
style={{
position: 'absolute',
bottom: 20,
right: 20,
backgroundColor: '#ff5a5f',
color: 'white',
padding: '10px 20px',
borderRadius: '20px',
cursor: 'pointer',
zIndex: 10,
}}
>
Scroll to Unread Messages
</button>
)}
</div>
); );
}; };