From acdc1315b3ef98c0d2fcad79627d38658e0ecb87 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Fri, 1 Mar 2024 14:00:52 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=8F=91=E9=80=81=E5=A4=B1=E8=B4=A5;=20?= =?UTF-8?q?=E4=B8=80=E4=BA=9B=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/msgUtils.js | 77 +++++++++---- src/stores/ConversationStore.js | 5 +- .../Components/Input/ImageUpload.jsx | 107 +++++++++++++++--- .../Components/InputComposer.jsx | 48 +++++--- .../Components/MessagesHeader.jsx | 2 +- src/views/Conversations/Conversations.css | 1 + 6 files changed, 182 insertions(+), 58 deletions(-) diff --git a/src/lib/msgUtils.js b/src/lib/msgUtils.js index bde7998..dbfdc6c 100644 --- a/src/lib/msgUtils.js +++ b/src/lib/msgUtils.js @@ -29,6 +29,32 @@ export const autoLinkText = (text) => { "msgcontent": "{\"body\":\"txtmsgtest\"}" } */ +const mediaMsg = { + contentToSend: (msg) => ({ + action: 'message', + actionId: msg.id, + renderId: msg.id, + to: msg.to, + msgcontent: { + [msg.type]: { link: msg.data.dataUri, caption: msg.text }, + ...(msg.context ? { context: msg.context, message_origin: msg.message_origin.msgOrigin } : {}), + }, + }), + contentToRender: (msg) => ({ + ...msg, + actionId: msg.id, + conversationid: msg.id.split('.')[0], + ...(msg.context + ? { + reply: { + message: msg.message_origin.text, + title: msg.message_origin.senderName || 'Reference', + titleColor: msg.message_origin?.senderName !== 'me' ? '#a791ff' : '#128c7e', + }, + } + : {}), + }), +}; export const sentMsgTypeMapped = { text: { type: 'text', @@ -44,14 +70,14 @@ export const sentMsgTypeMapped = { ...msg, actionId: msg.id, conversationid: msg.id.split('.')[0], - originText: (msg.text), + originText: msg.text, text: autoLinkText(msg.text), ...(msg.context ? { reply: { message: msg.message_origin.text, title: msg.message_origin.senderName || 'Reference', - titleColor: msg.message_origin?.senderName !== 'me' ? '#a791ff' : "#128c7e", + titleColor: msg.message_origin?.senderName !== 'me' ? '#a791ff' : '#128c7e', // titleColor: "#a791ff", }, } @@ -61,25 +87,23 @@ export const sentMsgTypeMapped = { photo: { type: 'image', contentToSend: (msg) => ({ - action: 'message', - actionId: msg.id, - renderId: msg.id, - to: msg.to, + ...mediaMsg.contentToSend({...msg, type: 'image'}), msgtype: 'image', - msgcontent: { - image: { link: msg.data.uri, caption: msg.text }, - ...(msg.context ? { context: msg.context, message_origin: msg.message_origin } : {}), - }, }), contentToRender: (msg) => ({ ...msg, - actionId: msg.id, - conversationid: msg.id.split('.')[0], - ...(msg.context - ? { - reply: { message: msg.message_origin.text, title: msg.message_origin.senderName || 'Reference', titleColor: "#a791ff", }, - } - : {}), + ...mediaMsg.contentToRender(msg), + }), + }, + video: { + type: 'video', + contentToSend: (msg) => ({ + ...mediaMsg.contentToSend(msg), + msgtype: 'video', + }), + contentToRender: (msg) => ({ + ...msg, + ...mediaMsg.contentToRender(msg), }), }, whatsappTemplate: { @@ -95,7 +119,7 @@ export const sentMsgTypeMapped = { conversationid: msg.id.split('.')[0], type: 'text', title: msg.template_origin.components.header?.[0]?.text || '', - text: autoLinkText( templateDataMapped?.body?.text || ''), // msg.template_origin.components.body?.[0]?.text || '', + text: autoLinkText(templateDataMapped?.body?.text || ''), // msg.template_origin.components.body?.[0]?.text || '', }; }, }, @@ -123,7 +147,7 @@ const whatsappMsgMapped = { contentObj = { ...contentObj, type: 'error', - text: {body: `❌ Message failed to send.` }, // contentObj.errorMessage + text: {body: `❌ ${whatsappError?.[contentObj.errorCode] || contentObj.errorMessage}` }, // contentObj.errorMessage // Message failed to send. id: contentObj.id, wamid: contentObj.id, }; @@ -138,7 +162,7 @@ const whatsappMsgMapped = { id: msgcontent.wamid, status: msgStatusRenderMapped[(msgcontent?.status || 'failed')], sender: 'me', - dateString: msgcontent.status==='failed' ? '发送失败 ❌' : '', + dateString: msgcontent.status==='failed' ? `发送失败 ${whatsappError?.[msgcontent.errorCode] || msgcontent.errorMessage} ❌` : '', }), }, }; @@ -167,7 +191,7 @@ export const receivedMsgTypeMapped = { // 发送消息的同步返回: 发送失败时 getMsg: (result) => result, contentToRender: () => null, - contentToUpdate: (msgcontent) => ({ ...msgcontent, id: msgcontent.actionId, status: msgcontent?.status || 'failed', dateString: '发送失败 ❌', conversationid: msgcontent.actionId.split('.')[0], }), + contentToUpdate: (msgcontent) => ({ ...msgcontent, id: msgcontent.actionId, status: msgcontent?.status || 'failed', dateString: `发送失败 ${msgcontent.error.message} ❌`, conversationid: msgcontent.actionId.split('.')[0], }), }, }; export const whatsappMsgTypeMapped = { @@ -274,7 +298,7 @@ export const parseRenderMessageItem = (msg) => { whatsapp_name: msg?.customerProfile?.name || '', whatsapp_phone_number: msg.from, whatsapp_msg_type: msg.type, - ...(isEmpty(msg.context) && isEmpty(msg.reaction) + ...((isEmpty(msg.context) && isEmpty(msg.reaction)) || msg.context.forwarded === true ? {} : { reply: { @@ -314,7 +338,7 @@ export const parseRenderMessageList = (messages, conversationid = null) => { sender: 'me', senderName: 'me', status: msgStatusRenderMapped[msgContent?.status || 'failed'], - dateString: msgStatusRenderMapped[msgContent?.status || 'failed'] === 'failed' ? '发送失败 ❌' : '', + dateString: msgStatusRenderMapped[msgContent?.status || 'failed'] === 'failed' ? `发送失败 ${whatsappError?.[msgContent.errorCode] || msgContent.errorMessage} ❌` : '', } : {}), ...(isEmpty(msg.messageorigin_AsJOSN) && isEmpty(msgContent.context) @@ -337,3 +361,10 @@ export const parseRenderMessageList = (messages, conversationid = null) => { }; }); }; +export const whatsappError = { + '131026': '消息无法投递. [未注册/使用旧版/未同意政策]', + '131047': '会话超过24小时.', + '131053': '文件上传失败.', + '131048': '账户被风控.', // 消息发送太多, 达到垃圾数量限制 + '131031': '账户已锁定.', +}; diff --git a/src/stores/ConversationStore.js b/src/stores/ConversationStore.js index 4bf89be..fc1df35 100644 --- a/src/stores/ConversationStore.js +++ b/src/stores/ConversationStore.js @@ -225,11 +225,12 @@ const messageSlice = (set, get) => ({ const { activeConversations, conversationsList, currentConversation } = get(); const targetMsgs = activeConversations[String(targetId)] || []; const targetIndex = conversationsList.findIndex((ele) => String(ele.sn) === String(targetId)); + const updateTime = message.type !== 'system' && message.sender !== 'me' ? message.date : null; const newConversation = targetId !== -1 ? { ...conversationsList[targetIndex], - last_received_time: message.type !== 'system' && message.sender !== 'me' ? message.date : conversationsList[targetIndex].last_received_time, + last_received_time: updateTime || conversationsList[targetIndex].last_received_time, unread_msg_count: String(targetId) !== String(currentConversation.sn) && message.sender !== 'me' ? conversationsList[targetIndex].unread_msg_count + 1 @@ -248,7 +249,7 @@ const messageSlice = (set, get) => ({ conversationsList: [...conversationsList], currentConversation: { ...currentConversation, - last_received_time: String(targetId) === String(currentConversation.sn) ? message.date : currentConversation.last_received_time, + last_received_time: String(targetId) === String(currentConversation.sn) ? updateTime : currentConversation.last_received_time, }, })); diff --git a/src/views/Conversations/Components/Input/ImageUpload.jsx b/src/views/Conversations/Components/Input/ImageUpload.jsx index fd4a0e4..d4489da 100644 --- a/src/views/Conversations/Components/Input/ImageUpload.jsx +++ b/src/views/Conversations/Components/Input/ImageUpload.jsx @@ -16,46 +16,119 @@ import { import useConversationStore from '@/stores/ConversationStore'; import { v4 as uuid } from 'uuid'; -const props = { - name: 'file', - action: 'https://run.mocky.io/v3/435e224c-44fb-4773-9faf-380c5e6a2188', - headers: { - authorization: 'authorization-text', - }, - showUploadList: false, +/** + * image + * ext: ani;bmp;gif;ico;jpe;jpeg;jpg;pcx;png;psd;tga;tif;tiff;wmf + * + * audio + * ext: aac;ac3;aif;aifc;aiff;au;cda;dts;fla;flac;it;m1a;m2a;m3u;m4a;mid;midi;mka;mod;mp2;mp3;mpa;ogg;ra;rmi;spc;rmi;snd;umx;voc;wav;wma;xm + * + * video + * ext: 3g2;3gp;3gp2;3gpp;amr;amv;asf;avi;bdmv;bik;d2v;divx;drc;dsa;dsm;dss;dsv;evo;f4v;flc;fli;flic;flv;hdmov;ifo;ivf;m1v;m2p;m2t;m2ts;m2v;m4b;m4p;m4v;mkv;mp2v;mp4;mp4v;mpe;mpeg;mpg;mpls;mpv2;mpv4;mov;mts;ogm;ogv;pss;pva;qt;ram;ratdvd;rm;rmm;rmvb;roq;rpm;smil;smk;swf;tp;tpr;ts;vob;vp6;webm;wm;wmp;wmv + */ +const fileTypes = { + 'photo': ['ani','bmp','gif','ico','jpe','jpeg','jpg','pcx','png','psd','tga','tif','tiff','wmf'], + 'audio': ['aac','ac3','aif','aifc','aiff','au','cda','dts','fla','flac','it','m1a','m2a','m3u','m4a','mid','midi','mka','mod','mp2','mp3','mpa','ogg','ra','rmi','spc','rmi','snd','umx','voc','wav','wma','xm'], + 'video': ['3g2','3gp','3gp2','3gpp','amr','amv','asf','avi','bdmv','bik','d2v','divx','drc','dsa','dsm','dsv','evo','f4v','flc','fli','flic','flv','hdmov','ifo','ivf','m1v','m2p','m2t','m2ts','m2v','m4b','m4p','m4v','mkv','mp2v','mp4','mp4v','mpe','mpeg','mpg','mpls','mpv2','mpv4','mov','mts','ogm','ogv','pss','pva','qt','ram','ratdvd','rm','rmm','rmvb','roq','rpm','smil','smk','swf','tp','tpr','ts','vob','vp6','webm','wm','wmp','wmv'], }; +const mockGetOSSData = () => ({ + dir: 'user-dir/', + expire: '1577811661', + host: '//haina-sale-system.oss-cn-shenzhen.aliyuncs.com', + accessId: 'LTAI5t8FBxiMYTd4tBSZzVy5', + // todo: + policy: 'eGl4aWhhaGFrdWt1ZGFkYQ==', + signature: 'ZGFob25nc2hhbw==', +}); + const ImageUpload = ({ disabled, invokeSendMessage }) => { - const { currentConversation, setComplexMsg } = useConversationStore(); + const { currentConversation, setComplexMsg, complexMsg } = useConversationStore(); const [uploading, setUploading] = useState(false); + const [OSSData, setOSSData] = useState(); - const handleSendImage = (src) => { + const handleSendImage = (file) => { + console.log(file); const msgObj = { - type: 'photo', - data: { uri: src, width: '100%', height: 150, }, + type: file.type, // 'photo', + name: file.name, + status: 'loading', + data: { uri: file.previewSrc, dataUri: file.dataUri, width: '100%', height: 150 }, id: uuid(), }; setComplexMsg(msgObj); // invokeSendMessage(msgObj); }; + const beforeUpload = async (file) => { + console.log('beforeUpload', file); + // 使用 FileReader 读取文件对象 + const reader = new FileReader(); + // let ret = true; + // 读取完毕后获取结果 + reader.onload = (event) => { + const previewSrc = event.target.result; + // test: src + const dataUri = `https://images.chinahighlights.com/allpicture/2020/04/9330cd3c78a34c81afd3b1fb.jpg`; + // const dataUri = `https://data.chinahighlights.com/video/CH-homepage-video.mp4`; + const suffix = file.name.slice(file.name.lastIndexOf('.')+1); + const type = Object.keys(fileTypes).find((type) => fileTypes[type].includes(suffix)); + const name = file.name; + // ret = type === 'photo'; + // const filename = Date.now() + suffix; + // file.url = OSSData.dir + filename; + handleSendImage({ previewSrc, dataUri, type, suffix, name}); + }; + // 把文件对象作为一个 dataURL 读入 + reader.readAsDataURL(file); + return file; + // return ret ? file : false; + + // if (!OSSData) return false; + // const expire = Number(OSSData.expire) * 1000; + // if (expire < Date.now()) { + // // await init(); + // const result = await mockGetOSSData(); + // setOSSData(result); + // } + // const suffix = file.name.slice(file.name.lastIndexOf('.')); + // const filename = Date.now() + suffix; + // file.url = OSSData.dir + filename; + // return file; + }; + const uploadProps = { + name: 'file', + action: 'https://run.mocky.io/v3/435e224c-44fb-4773-9faf-380c5e6a2188', + // action: OSSData?.host, + headers: { + authorization: 'authorization-text', + }, + showUploadList: false, + // onChange: handleChange, + // onRemove, + // data: getExtraData, + beforeUpload, + }; return ( { + console.log('fileList', info.fileList); setUploading(info.file.status === 'uploading'); + setComplexMsg({...complexMsg, status: 'loading'}) if (info.file.status !== 'uploading') { console.log(info.file, info.fileList); + setComplexMsg({...complexMsg, status: info.file.status}) } if (info.file.status === 'done') { + // todo: 会闪烁 + setComplexMsg({...complexMsg, status: info.file.status, data: { ...complexMsg.data, uri: complexMsg.data.dataUri}}) // message.success(`${info.file.name} file uploaded successfully`); - // test: src - // handleSendImage('blob:https://web.whatsapp.com/bbe878fc-7bde-447f-aa28-a4b929621a50'); - handleSendImage('https://images.chinahighlights.com//allpicture/2020/04/9330cd3c78a34c81afd3b1fb.jpg'); } else if (info.file.status === 'error') { message.error(`图片添加失败`); } - }}> -