From 0839335c2df4698c00fe2a5723a657410f29a7f5 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 8 Mar 2024 15:46:39 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E7=9A=84?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E9=99=90=E5=88=B6;=E9=AB=98=E5=BE=B7?= =?UTF-8?q?=E5=9C=B0=E5=9B=BE;=20ChatHistory;=E4=BC=9A=E8=AF=9D=E5=88=97?= =?UTF-8?q?=E8=A1=A8:=20=E6=98=BE=E7=A4=BA=E8=AE=A2=E5=8D=95=E5=8F=B7;=20?= =?UTF-8?q?=E6=9C=AA=E8=AF=BB=E6=B6=88=E6=81=AF=E8=AE=BE=E7=BD=AE=E5=B7=B2?= =?UTF-8?q?=E8=AF=BB=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/actions/ConversationActions.js | 8 +- src/lib/msgUtils.js | 5 +- src/views/AuthApp.jsx | 2 +- src/views/ChatHistory.jsx | 292 +++++++++--------- .../Components/ConversationsList.jsx | 3 +- .../Components/Input/MediaUpload.jsx | 1 + .../Conversations/Components/MessagesList.jsx | 110 +++---- .../Components/MessagesWrapper.jsx | 28 +- src/views/Conversations/Conversations.css | 2 +- 9 files changed, 237 insertions(+), 214 deletions(-) diff --git a/src/actions/ConversationActions.js b/src/actions/ConversationActions.js index 5f6c809..620cf1e 100644 --- a/src/actions/ConversationActions.js +++ b/src/actions/ConversationActions.js @@ -37,7 +37,11 @@ export const fetchOrderConversationsList = async (params) => { }; export const fetchConversationItemClose = async (body) => { - const getParams = body ? new URLSearchParams(body).toString() : ''; - const { result } = await fetchJSON(`${API_HOST}/closeconversation?${getParams}`); + const { result } = await fetchJSON(`${API_HOST}/closeconversation`, body); return result; }; + +export const fetchCleanUnreadMsgCount = async (params) => { + const { errcode, result } = await fetchJSON(`${API_HOST}/clean_unread_msg_count`, params); + return errcode !== 0 ? {} : result; +}; diff --git a/src/lib/msgUtils.js b/src/lib/msgUtils.js index 6212dd1..0899bc3 100644 --- a/src/lib/msgUtils.js +++ b/src/lib/msgUtils.js @@ -266,9 +266,6 @@ export const whatsappMsgTypeMapped = { }, }, originText: msg.image?.caption || '', - onOpen: () => { - console.log('Open image', msg.image.link); - }, }), renderForReply: (msg) => ({ id: msg.wamid, @@ -377,7 +374,7 @@ export const whatsappMsgTypeMapped = { id: msg.wamid, title: `位置信息 ${msg.location.name || ''} ↓打开高德地图`, text: msg.location.address, // 地址 - src: `https://uri.amap.com/marker?position=${msg.location.longitude},${msg.location.latitude}`, + src: `https://uri.amap.com/marker?position=${msg.location.longitude},${msg.location.latitude}&callnative=1`, data: { longitude: msg.location?.longitude, latitude: msg.location?.latitude, diff --git a/src/views/AuthApp.jsx b/src/views/AuthApp.jsx index b21a390..9c85960 100644 --- a/src/views/AuthApp.jsx +++ b/src/views/AuthApp.jsx @@ -70,7 +70,7 @@ function AuthApp() { let interval; if (totalNotify > 0) { interval = setInterval(() => { - document.title = isTitleVisible ? `🔔🔥💬【${totalNotify}条新消息】` : '聊天式销售平台'; + document.title = isTitleVisible ? `🔔🔥💬【${totalNotify}条新消息】` : '______________'; setIsTitleVisible(!isTitleVisible); }, 500); } else { diff --git a/src/views/ChatHistory.jsx b/src/views/ChatHistory.jsx index 5d38fbf..9a819b4 100644 --- a/src/views/ChatHistory.jsx +++ b/src/views/ChatHistory.jsx @@ -1,170 +1,167 @@ -import { useNavigate } from 'react-router-dom' +import { useNavigate } from 'react-router-dom'; import { memo, useCallback, useEffect, useRef, useState } from 'react'; -import { Row, Col, Divider, Table , Card, Button, Input, - Space, Empty, Radio, Select, DatePicker, Form, List, Avatar -} from 'antd' -import { - StarFilled, ZoomInOutlined, StarOutlined, SearchOutlined -} from '@ant-design/icons' +import { Row, Col, Divider, Table, Card, Button, Input, Flex, Layout, Space, Empty, Radio, Select, DatePicker, Form, List, Avatar } from 'antd'; +import { StarFilled, ZoomInOutlined, StarOutlined, SearchOutlined } from '@ant-design/icons'; +import { ChatList, ChatItem, MessageBox } from 'react-chat-elements'; -const { Search } = Input -const { RangePicker } = DatePicker +const { Sider, Content, Header, Footer } = Layout; +const { Search } = Input; +const { RangePicker } = DatePicker; const data = [ { title: 'Ann', avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=0', msgTime: '03-04 13:45:29', - content: 'Hi, this is Ann from China Highlights travel and it is my pleasure to help your Beijing trip. I have sent you an email with the general idea about you trip. Please check it and if any question or new idea just let me know. We are specailized in customizing tour and I would like to work with you to create a tour itinerary that you like. Look forward to your reply.' + content: + 'Hi, this is Ann from China Highlights travel and it is my pleasure to help your Beijing trip. I have sent you an email with the general idea about you trip. Please check it and if any question or new idea just let me know. We are specailized in customizing tour and I would like to work with you to create a tour itinerary that you like. Look forward to your reply.', }, { title: 'David Azhari', avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2', msgTime: '03-04 16:33:08', - content: 'Hi! I just replied to your email saying that I have a few questions and notes' + content: 'Hi! I just replied to your email saying that I have a few questions and notes', }, { title: 'David Azhari', avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2', msgTime: '03-04 16:33:34', - content: 'So first, is it possible for you guys to write a PU Invitation letter for visas or no?' + content: 'So first, is it possible for you guys to write a PU Invitation letter for visas or no?', }, { title: 'Ann', avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=0', msgTime: '03-04 16:33:41', - content: 'you prefer we discuss here or go on by mail ?' + content: 'you prefer we discuss here or go on by mail ?', }, { title: 'David Azhari', avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2', msgTime: '03-04 16:34:03', - content: 'I prefer by mail if it’s ok with you' + content: 'I prefer by mail if it’s ok with you', }, { title: 'Ann', avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=0', msgTime: '03-04 16:34:28', - content: 'both is okay ,I am typing mail to you now lol and receive your message here.' + content: 'both is okay ,I am typing mail to you now lol and receive your message here.', }, { title: 'David Azhari', avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=2', msgTime: '03-05 02:56:37', - content: 'Ok thank you so much' + content: 'Ok thank you so much', }, { title: 'Ann', avatarUrl: 'https://api.dicebear.com/7.x/miniavs/svg?seed=0', msgTime: '03-04 16:44:03', - content: 'you are welcome I have sent the mail to you ,please check.it is my pleasure to assist you with your tour.' + content: 'you are welcome I have sent the mail to you ,please check.it is my pleasure to assist you with your tour.', }, -] +]; +// eslint-disable-next-line react/display-name const SearchForm = memo(function ({ onSubmit }) { - const [form] = Form.useForm() + const [form] = Form.useForm(); function handleSubmit(values) { - onSubmit?.(values) + onSubmit?.(values); } return (
- - (option?.label ?? '').toLowerCase().includes(input.toLowerCase())} + options={[ + { + value: '杨新玲', + label: '杨新玲', + }, + { + value: '黄雪荣', + label: '黄雪荣', + }, + { + value: '骆梅玉', + label: '骆梅玉', + }, + { + value: '秦宇尘', + label: '秦宇尘', + }, + { + value: '莫梦瑶', + label: '莫梦瑶', + }, + { + value: '秦雯萱', + label: '秦雯萱', + }, + { + value: '王露加', + label: '王露加', + }, + ]} + /> - - (option?.label ?? '').toLowerCase().includes(input.toLowerCase())} + options={[ + { + value: 'Denise', + label: 'Denise', + }, + { + value: 'Kennedy', + label: 'Kennedy', + }, + { + value: 'Harsh', + label: 'Harsh', + }, + { + value: 'SMLiew', + label: 'SMLiew', + }, + { + value: 'See Kok Ching', + label: 'See Kok Ching', + }, + { + value: 'Jonathan Michael Lilley', + label: 'Jonathan Michael Lilley', + }, + { + value: 'Loan', + label: 'Loan', + }, + { + value: 'Leah Belle', + label: 'Leah Belle', + }, + { + value: 'Christelle Narvasa', + label: 'Christelle Narvasa', + }, + { + value: 'Mai Schaefer', + label: 'Mai Schaefer', + }, + ]} + /> @@ -172,45 +169,62 @@ const SearchForm = memo(function ({ onSubmit }) { - - + + - ) -}) + ); +}); function ChatHistory() { - - const [formValues, setFormValues] = useState({}) + const [formValues, setFormValues] = useState({}); const handleSubmit = useCallback((values) => { - setFormValues({...values}) - }, []) - + setFormValues({ ...values }); + }, []); + return ( <> -
- ( - - } - title={{item.title}} - description={item.msgTime} + + + + + +
+ { + console.log('load more'); + }} + className='h-full overflow-y-auto px-2' + itemLayout='vertical' + dataSource={data} + renderItem={(item, index) => ( + + } title={item.title} description={item.msgTime} /> +
{item.content}
+
+ )} /> -
{item.content}
- - - )} - /> -
+
+ + - ) + ); } -export default ChatHistory +export default ChatHistory; diff --git a/src/views/Conversations/Components/ConversationsList.jsx b/src/views/Conversations/Components/ConversationsList.jsx index 58336f7..201c2d8 100644 --- a/src/views/Conversations/Components/ConversationsList.jsx +++ b/src/views/Conversations/Components/ConversationsList.jsx @@ -115,11 +115,10 @@ const Conversations = () => { {...item} key={item.sn} id={item.sn} - // avatar={`https://api.dicebear.com/7.x/avataaars/svg?seed=${item.whatsapp_name.trim() || item.whatsapp_phone_number}`} letterItem={{id: item.whatsapp_name.trim() || item.whatsapp_phone_number, letter: (item.whatsapp_name.trim() || item.whatsapp_phone_number).slice(0, 5)}} alt={`${item.whatsapp_name.trim()}`} title={item.whatsapp_name.trim() || item.whatsapp_phone_number} - subtitle={item.whatsapp_phone_number} + subtitle={item.coli_id} date={item.last_received_time} unread={item.unread_msg_count} className={String(item.sn) === String(currentConversation.sn) ? '__active text-primary border-y-0 border-e-0 border-s-4 border-solid border-whatsapp-bg bg-whatsapp-bg' : ''} diff --git a/src/views/Conversations/Components/Input/MediaUpload.jsx b/src/views/Conversations/Components/Input/MediaUpload.jsx index 863c2e5..0012bf0 100644 --- a/src/views/Conversations/Components/Input/MediaUpload.jsx +++ b/src/views/Conversations/Components/Input/MediaUpload.jsx @@ -64,6 +64,7 @@ const ImageUpload = ({ disabled, invokeUploadFileMessage, invokeSendUploadMessag showUploadList: false, beforeUpload, maxCount: 1, + accept: 'audio/aac, audio/mp4, audio/mpeg, audio/amr, audio/ogg,image/jpeg, image/png,video/mp4, video/3gp,image/webp,text/plain, application/pdf, application/vnd.ms-powerpoint, application/msword, application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.openxmlformats-officedocument.presentationml.presentation, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }; return ( { +const MessagesList = ({ messages, handlePreview, reference }) => { const setReferenceMsg = useConversationStore(useShallow((state) => state.setReferenceMsg)); - olog('render message list'); - - const scrollToMessage = (id, index) => { - const _i = index || props.dataSource.findIndex((msg) => msg.id === id); - if (_i >= 0) { - messageRefs.current[_i].current.scrollIntoView({ behavior: 'smooth', block: 'center' }); - } - }; + // const messagesEndRef = useRef(null); const messageRefs = useRef([]); - messageRefs.current = props.dataSource.map((_, i) => messageRefs.current[i] ?? createRef()); + const [page, setPage] = useState(1); + let timeout = null; - const toBottom = (e) => { - if (!reference) return; - reference.current.scrollTop = reference.current.scrollHeight - reference.current.offsetHeight; + const fetchNextPage = async () => { + olog('fetchNextPage') + setPage(page + 1); + // Fetch next page of messages here }; - const prevProps = useRef(props); - useEffect(() => { - if (prevProps.current.dataSource.length !== props.dataSource.length) { - toBottom(); + const handleScroll = (e) => { + const { scrollTop } = e.target; + const delay = 1000; // 1 second + + if (scrollTop === 0) { + if (timeout) clearTimeout(timeout); + timeout = setTimeout(fetchNextPage, delay); } + }; - prevProps.current = props; - }, [prevProps, props]); + const scrollToBottom = () => { + if (reference.current) { + reference.current.scrollTop = reference.current.scrollHeight; + } + }; - const [previewVisible, setPreviewVisible] = useState(false); - const [previewSrc, setPreviewSrc] = useState(); - const onPreviewClose = () => { - setPreviewSrc(''); - setPreviewVisible(false); + 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; + } }; - const handlePreview = (msg) => { - switch (msg.type) { - case 'photo': - setPreviewVisible(true); - setPreviewSrc(msg.data.uri); - return false; - case 'file': - window.open(msg.data.link || msg.data.uri, '_blank', 'noopener,noreferrer'); - return false; + useEffect(scrollToBottom, [messages]); - default: - return false; + 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 !== ''); @@ -88,6 +89,7 @@ const MessagesList = ({ reference, contactsModalOpen, setContactsModalOpen, ...p ); }); + // eslint-disable-next-line react/display-name const MessageBoxWithRef = forwardRef((props, ref) => (
@@ -96,27 +98,11 @@ const MessagesList = ({ reference, contactsModalOpen, setContactsModalOpen, ...p )); return ( -
-
- {props.dataSource.map((message, index) => ( - // { - // domEvent.stopPropagation(); - // switch (key) { - // case 'reply': - // return setReferenceMsg(message); - - // default: - // return; - // } - // }, - // }} - // trigger={['contextMenu']}> +
+
+ {messages.map((message, index) => ( (messageRefs.current[index] = el)} key={message.id} {...message} position={message.sender === 'me' ? 'right' : 'left'} @@ -149,11 +135,9 @@ const MessagesList = ({ reference, contactsModalOpen, setContactsModalOpen, ...p } : {})} /> - // ))}
-
); }; diff --git a/src/views/Conversations/Components/MessagesWrapper.jsx b/src/views/Conversations/Components/MessagesWrapper.jsx index 362bbbe..84b1323 100644 --- a/src/views/Conversations/Components/MessagesWrapper.jsx +++ b/src/views/Conversations/Components/MessagesWrapper.jsx @@ -1,15 +1,39 @@ -import { useRef } from 'react'; +import { useRef, useState } from 'react'; import useConversationStore from '@/stores/ConversationStore'; import { useShallow } from 'zustand/react/shallow'; +import { Image, } from 'antd'; import MessagesList from './MessagesList'; const MessagesWrapper = () => { const activeMessages = useConversationStore(useShallow((state) => (state.currentConversation.sn && state.activeConversations[state.currentConversation.sn] ? state.activeConversations[state.currentConversation.sn]: []))); const reference = useRef(null); + + const [previewVisible, setPreviewVisible] = useState(false); + const [previewSrc, setPreviewSrc] = useState(); + const onPreviewClose = () => { + setPreviewSrc(''); + setPreviewVisible(false); + }; + const handlePreview = (msg) => { + switch (msg.whatsapp_msg_type) { + case 'image': + setPreviewVisible(true); + setPreviewSrc(msg.data.uri); + return false; + + case 'document': + window.open(msg.data.link || msg.data.uri, '_blank', 'noopener,noreferrer'); + return false; + + default: + return false; + } + }; return ( <> - + + ); }; diff --git a/src/views/Conversations/Conversations.css b/src/views/Conversations/Conversations.css index d7efac4..c3654c4 100644 --- a/src/views/Conversations/Conversations.css +++ b/src/views/Conversations/Conversations.css @@ -67,7 +67,7 @@ .chatwindow-wrapper .rce-mbox-reply-message, .chatwindow-wrapper .emoji { - font-family: "Noto Color Emoji", 'Apple Color Emoji', 'Twemoji Mozilla', 'Segoe UI Emoji', 'Segoe UI Symbol', 'EmojiOne Color', 'Android Emoji', sans-serif; + font-family: "Noto Color Emoji", 'Apple Color Emoji', 'Twemoji Mozilla', 'Segoe UI Emoji', 'Segoe UI Symbol', 'EmojiOne Color', 'Android Emoji', Arial, sans-serif; font-weight: 500; } .chatwindow-wrapper .rce-mbox-text a{