You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Global-sales/src/views/Conversations/Components/MessagesList.jsx

146 lines
5.3 KiB
React

import { useEffect, useRef, useState, forwardRef, memo } from 'react';
import { MessageBox } from 'react-chat-elements';
import { Button } from 'antd';
import { DownOutlined } from '@ant-design/icons';
import { useShallow } from 'zustand/react/shallow';
import useConversationStore from '@/stores/ConversationStore';
import { isEmpty, olog } from '@/utils/utils';
const MessagesList = ({ messages, handlePreview, reference }) => {
const setReferenceMsg = useConversationStore(useShallow((state) => state.setReferenceMsg));
// const messagesEndRef = useRef(null);
const messageRefs = useRef([]);
const [page, setPage] = useState(1);
let timeout = null;
const fetchNextPage = async () => {
olog('fetchNextPage')
setPage(page + 1);
// Fetch next page of messages here
};
const handleScroll = (e) => {
const { scrollTop } = e.target;
const delay = 1000; // 1 second
if (scrollTop === 0) {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(fetchNextPage, delay);
}
};
const scrollToBottom = () => {
if (reference.current) {
reference.current.scrollTop = reference.current.scrollHeight;
}
};
const scrollToMessage = (id, index) => {
const _i = index || messages.findIndex((msg) => msg.id === id);
if (reference.current && messageRefs.current[_i]) {
reference.current.scrollTop = messageRefs.current[_i].offsetTop;
}
};
useEffect(scrollToBottom, [messages]);
useEffect(() => {
const messageList = reference.current;
if (messageList) {
messageList.addEventListener('scroll', handleScroll);
}
return () => {
if (messageList) {
messageList.removeEventListener('scroll', handleScroll);
}
};
}, []);
const RenderText = memo(function renderText({ str }) {
const parts = str.split(/(https?:\/\/[^\s]+|\p{Emoji_Presentation})/gmu).filter((s) => s !== '');
const links = str.match(/https?:\/\/[\S]+/gi) || [];
const emojis = str.match(/([\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])/g) || [];
const extraClass = isEmpty(emojis) ? '' : 'text-base leading-5 emoji-text';
const objArr = parts.reduce((prev, curr, index) => {
if (links.includes(curr)) {
prev.push({ type: 'link', key: curr });
} else if (emojis.includes(curr)) {
prev.push({ type: 'emoji', key: curr });
} else {
prev.push({ type: 'text', key: curr });
}
return prev;
}, []);
return (
<span className={extraClass}>
{(objArr || []).map((part, index) => {
if (part.type === 'link') {
return (
<a href={part.key} target='_blank' key={`${part.key}${index}`} rel='noreferrer' className='text-sm'>
{part.key}
</a>
);
} else {
// if (part.type === 'emoji')
return part.key;
}
})}
</span>
);
});
// eslint-disable-next-line react/display-name
const MessageBoxWithRef = forwardRef((props, ref) => (
<div ref={ref}>
<MessageBox {...props} />
</div>
));
return (
<div className='relative h-full overflow-y-auto overflow-x-hidden flex flex-1'>
<div ref={reference} className='relative overflow-y-auto overflow-x-hidden block flex-1'>
{messages.map((message, index) => (
<MessageBoxWithRef
ref={(el) => (messageRefs.current[index] = el)}
key={message.id}
{...message}
position={message.sender === 'me' ? 'right' : 'left'}
onReplyClick={() => setReferenceMsg(message)}
onReplyMessageClick={() => scrollToMessage(message.reply.id)}
onOpen={() => handlePreview(message)}
onTitleClick={() => handlePreview(message)}
text={<RenderText str={message?.text || ''} />}
{...(message.sender === 'me'
? {
styles: { backgroundColor: '#ccd4ae' },
notchStyle: { fill: '#ccd4ae' },
replyButton: ['text', 'document', 'image'].includes(message.whatsapp_msg_type) && message.status !== 'failed' ? true : false,
className: 'whatsappme-container whitespace-pre-wrap',
}
: {
replyButton: ['text', 'document', 'image'].includes(message.whatsapp_msg_type) ? true : false,
className: ['whitespace-pre-wrap', message.whatsapp_msg_type === 'sticker' ? 'bg-transparent' : ''].join(' '),
})}
{...(message.type === 'meetingLink'
? {
actionButtons: [
{
onClickButton: () => {
navigator.clipboard.writeText(message.text);
},
Component: () => <div>复制</div>,
},
],
}
: {})}
/>
))}
</div>
<Button onClick={scrollToBottom} ghost type={'dashed'} shape={'circle'} className=' absolute bottom-1 right-4' icon={<DownOutlined />} />
</div>
);
};
export default MessagesList;