From c466ac953b0e5502d9177781f3d7f3548e3f57ca Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 25 Mar 2024 09:24:24 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E6=A8=A1=E6=9D=BF:=20=E6=9C=89header,?= =?UTF-8?q?=20footer,=20buttons?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit perf: 历史记录: 显示绝对时间 fix: 模板: 只发送body perf: 提示文字 style: 历史记录: 引用的来源title color perf: 历史记录: 会话的的时间 --- src/actions/ConversationActions.js | 10 +++ src/lib/msgUtils.js | 12 ++-- src/views/ChatHistory.jsx | 7 ++- .../Components/ExpireTimeClock.jsx | 2 - .../Components/Input/MediaUpload.jsx | 2 +- .../Components/Input/Template.jsx | 61 ++++++++++++++++--- .../Components/InputComposer.jsx | 2 +- .../Conversations/Components/MessagesList.jsx | 46 +++++++++++++- 8 files changed, 120 insertions(+), 22 deletions(-) diff --git a/src/actions/ConversationActions.js b/src/actions/ConversationActions.js index e48059e..cede50e 100644 --- a/src/actions/ConversationActions.js +++ b/src/actions/ConversationActions.js @@ -4,6 +4,7 @@ import { fetchJSON, postJSON } from '@/utils/request' import { parseRenderMessageList } from '@/lib/msgUtils'; import { API_HOST } from '@/config'; import { isEmpty } from '@/utils/commons'; +import dayjs from 'dayjs'; export const fetchTemplates = async () => { const data = await fetchJSON(`${API_HOST}/listtemplates`); @@ -62,6 +63,14 @@ export const fetchConversationItemClose = async (body) => { return result; }; +/** + * @param {object} body { phone_number, name } + */ +export const postNewConversationItem = async (body) => { + const { errcode, result } = await postJSON(`${API_HOST}/newconversation`, body); + return errcode !== 0 ? {} : result; +}; + /** * @param {object} params { opisn, whatsappid } */ @@ -89,6 +98,7 @@ export const fetchConversationsSearch = async (params) => { whatsapp_name: `${ele.whatsapp_name || ''}`.trim(), opi_sn: ele.OPI_SN || ele.opi_sn || 0, OPI_Name: `${ele.OPI_Name || ele.opi_name || ''}`.trim(), + dateText: dayjs((ele.last_received_time || ele.last_send_time)).format('MM-DD HH:mm'), matchMsgList: parseRenderMessageList((ele.msglist_AsJOSN || [])), // .reverse()), })); return list; diff --git a/src/lib/msgUtils.js b/src/lib/msgUtils.js index e82cf39..0610874 100644 --- a/src/lib/msgUtils.js +++ b/src/lib/msgUtils.js @@ -1,4 +1,5 @@ import { cloneDeep, isEmpty, olog } from "@/utils/utils"; +import dayjs from "dayjs"; import { v4 as uuid } from "uuid"; export const replaceTemplateString = (str, replacements) => { @@ -150,7 +151,7 @@ export const sentMsgTypeMapped = { }), }, whatsappTemplate: { - contentToSend: (msg) => ({ action: 'message', actionId: msg.id, renderId: msg.id, to: msg.to, msgtype: 'template', msgcontent: msg.template }), + contentToSend: (msg) => ({ action: 'message', actionId: msg.id, renderId: msg.id, to: msg.to, msgtype: 'template', msgcontent: { ...msg.template, components: msg.template.components.filter(com => com.type.toLowerCase() === 'body') }}), // todo: 其他组件不发送是否可以 contentToRender: (msg) => { const templateDataMapped = msg.template?.components ? msg.template.components.reduce((r, v) => ({ ...r, [v.type]: v }), {}) : null; // const templateParam = (templateDataMapped?.body?.parameters || []).map(e => e.text); @@ -161,7 +162,7 @@ export const sentMsgTypeMapped = { actionId: msg.id, conversationid: msg.id.split('.')[0], type: 'text', - title: msg.template_origin.components.header?.[0]?.text || '', + title: msg.template.name, // || msg.template_origin.components.header?.[0]?.text || '', text: autoLinkText(templateDataMapped?.body?.text || ''), // msg.template_origin.components.body?.[0]?.text || '', }; }, @@ -462,7 +463,7 @@ export const parseRenderMessageItem = (msg) => { /** * 从数据库读取的记录 */ -export const parseRenderMessageList = (messages, conversationid = null) => { +export const parseRenderMessageList = (messages) => { return messages.map((msg, i) => { let msgContentString = ''; if (typeof msg.msgtext_AsJOSN === 'string') { @@ -480,6 +481,7 @@ export const parseRenderMessageList = (messages, conversationid = null) => { type: msgContent.type, ...(typeof whatsappMsgTypeMapped[msgType].type === 'function' ? whatsappMsgTypeMapped[msgType].type(msg) : { type: whatsappMsgTypeMapped[msgType].type || 'text' }), date: msgContent?.sendTime || msg.msgtime || '', + dateText: dayjs(msgContent?.sendTime || msg.msgtime).format('MM-DD HH:mm'), localDate: (msg.msgtime || '').replace('T', ' '), from: msgContent.from, sender: msgContent.from, @@ -505,8 +507,8 @@ export const parseRenderMessageList = (messages, conversationid = null) => { ...(typeof whatsappMsgTypeMapped[(msg.messageorigin_AsJOSN?.type || 'unsupported')]?.renderForReply === 'function' ? whatsappMsgTypeMapped[(msg.messageorigin_AsJOSN?.type || 'unsupported')].renderForReply(msg.messageorigin_AsJOSN) : {}), - // titleColor: msg.messageorigin_AsJOSN?.customerProfile?.name ? '#a791ff' : "#128c7e", - titleColor: msg.messageorigin_direction === 'inbound' ? '#a791ff' : "#128c7e", + titleColor: msg.messageorigin_AsJOSN?.customerProfile?.name ? '#a791ff' : "#128c7e", + // titleColor: msg.messageorigin_direction === 'inbound' ? '#a791ff' : "#128c7e", }, origin: msg.messageorigin_AsJOSN, }), diff --git a/src/views/ChatHistory.jsx b/src/views/ChatHistory.jsx index f355076..1eef08b 100644 --- a/src/views/ChatHistory.jsx +++ b/src/views/ChatHistory.jsx @@ -314,8 +314,9 @@ function ChatHistory() { alt={`${item.whatsapp_name}`} title={item.whatsapp_name || item.whatsapp_phone_number} subtitle={`${item.OPI_Name || ''} ${item.coli_id || ''}`} - date={item.last_received_time} + date={item.last_received_time || item.last_send_time} // dateString={item.last_received_time} + dateString={item.dateText} className={String(item.conversationid) === String(selectedConversation.conversationid) ? '__active text-primary bg-neutral-100' : ''} onClick={() => setSelectedConversation(item)} /> @@ -340,7 +341,7 @@ function ChatHistory() { title={item.sender === 'me' ? selectedConversation.OPI_Name || item.senderName : item.senderName} subtitle={item.originText} date={item.msgtime} - // dateString={item.msgtime} + dateString={item.dateText} className={String(item.sn) === String(selectMatch?.sn) ? '__active text-primary bg-neutral-100' : ' bg-white'} onClick={() => handleMatchMsgClick(item)} /> @@ -392,7 +393,7 @@ function ChatHistory() { navigator.clipboard.writeText(message.text); appMessage.success('复制成功😀'); }, - Component: () =>
复制
, + Component: () =>
复制
, }, ], } diff --git a/src/views/Conversations/Components/ExpireTimeClock.jsx b/src/views/Conversations/Components/ExpireTimeClock.jsx index a23907e..da3f2a3 100644 --- a/src/views/Conversations/Components/ExpireTimeClock.jsx +++ b/src/views/Conversations/Components/ExpireTimeClock.jsx @@ -1,7 +1,6 @@ import { useEffect, useState } from 'react'; import { Typography } from 'antd'; import { ClockCircleOutlined } from '@ant-design/icons'; -import useConversationStore from '@/stores/ConversationStore'; import dayjs from 'dayjs'; import utc from 'dayjs/plugin/utc'; @@ -13,7 +12,6 @@ dayjs.extend(timezone); dayjs.extend(relativeTime); const ExpireTimeClock = ({ expireTime }) => { - // const expireTime = useConversationStore((state) => state.currentConversation.conversation_expiretime); const [customerDateTime, setCustomerDateTime] = useState(''); const [isExpired, setIsExpired] = useState(false); diff --git a/src/views/Conversations/Components/Input/MediaUpload.jsx b/src/views/Conversations/Components/Input/MediaUpload.jsx index 64a4b2a..6084f67 100644 --- a/src/views/Conversations/Components/Input/MediaUpload.jsx +++ b/src/views/Conversations/Components/Input/MediaUpload.jsx @@ -80,7 +80,7 @@ const ImageUpload = ({ disabled, invokeUploadFileMessage, invokeSendUploadMessag } }} > - +
webp(100K)
图片(5M)
视频(16M)
语音(16M)
附件(100M)
} > + ) : btn.type.toLowerCase() === 'phone_number' ? ( + + ) : ( + + ) + )} + + ); + } + const renderForm = ({ tempItem }) => { const templateText = tempItem.components.body?.[0]?.text || ''; const tempArr = splitTemplate(templateText); @@ -167,8 +212,10 @@ const InputTemplate = ({ mobile, disabled = false, invokeSendMessage }) => { description={ <>
-
{renderForm({ tempItem: item })}
+ {renderHeader({ tempItem: item })} +
{renderForm({ tempItem: item })}
{item.components?.footer?.[0] ?
{item.components.footer[0].text || ''}
: null} + {renderButtons({ tempItem: item })}
} diff --git a/src/views/Conversations/Components/InputComposer.jsx b/src/views/Conversations/Components/InputComposer.jsx index 686eedf..dabbf29 100644 --- a/src/views/Conversations/Components/InputComposer.jsx +++ b/src/views/Conversations/Components/InputComposer.jsx @@ -255,7 +255,7 @@ const InputComposer = ({ mobile }) => { showCount={textabled} placeholder={ gt24h - ? 'This session has expired. Please send a template message to activate the session' + ? '会话已过期. 请发送打招呼消息激活对话💬.' : mobile === undefined ? 'Enter 发送, Shift+Enter 换行\n支持复制粘贴 [截图/文件] 以备发送' : 'Enter 换行, 点击 Send 发送' diff --git a/src/views/Conversations/Components/MessagesList.jsx b/src/views/Conversations/Components/MessagesList.jsx index 6a8b7f6..a0df233 100644 --- a/src/views/Conversations/Components/MessagesList.jsx +++ b/src/views/Conversations/Components/MessagesList.jsx @@ -4,7 +4,7 @@ import { App, Button } from 'antd'; import { DownOutlined, LoadingOutlined } from '@ant-design/icons'; import { useShallow } from 'zustand/react/shallow'; import useConversationStore from '@/stores/ConversationStore'; -import { isEmpty, } from '@/utils/utils'; +import { groupBy, isEmpty, } from '@/utils/utils'; const MessagesList = ({ messages, handlePreview, reference, longListLoading, getMoreMessages, shouldScrollBottom, loadNextPage, handleContactClick, ...props }) => { @@ -32,7 +32,16 @@ const MessagesList = ({ messages, handlePreview, reference, longListLoading, get useEffect(scrollToBottom, [messages]); - const RenderText = memo(function renderText({ str, className }) { + const RenderText = memo(function renderText({ str, className, template }) { + + let headerObj, footerObj, buttonsArr; + if (!isEmpty(template)) { + const componentsObj = groupBy(template.components, (item) => item.type); + headerObj = componentsObj.header[0]; + footerObj = componentsObj.footer[0]; + buttonsArr = componentsObj.buttons.reduce((r, c) => r.concat(c.buttons), []); + } + 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) || []; @@ -49,6 +58,17 @@ const MessagesList = ({ messages, handlePreview, reference, longListLoading, get }, []); return ( + {headerObj ? ( +
+ {'text' === headerObj.format.toLowerCase() &&
{headerObj.text}
} + {'image' === headerObj.format.toLowerCase() && } + {['document', 'video'].includes(headerObj.format.toLowerCase()) && ( + + [ {headerObj.format} ] + + )} +
+ ) : null} {(objArr || []).map((part, index) => { if (part.type === 'link') { return ( @@ -61,6 +81,26 @@ const MessagesList = ({ messages, handlePreview, reference, longListLoading, get return part.key; } })} + {footerObj ?
{footerObj.text}
: null} + {buttonsArr && buttonsArr.length > 0 ? ( +
+ {buttonsArr.map((btn, index) => + btn.type.toLowerCase() === 'url' ? ( + + ) : btn.type.toLowerCase() === 'phone_number' ? ( + + ) : ( + + ) + )} +
+ ) : null}
); }); @@ -99,7 +139,7 @@ const MessagesList = ({ messages, handlePreview, reference, longListLoading, get onReplyMessageClick={() => scrollToMessage(message.reply.id)} onOpen={() => handlePreview(message)} onTitleClick={() => handlePreview(message)} - text={} + text={} {...(message.sender === 'me' ? { styles: { backgroundColor: '#ccd4ae' },