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/ConversationsList.jsx b/src/views/Conversations/Components/ConversationsList.jsx
index ea345e8..da5e751 100644
--- a/src/views/Conversations/Components/ConversationsList.jsx
+++ b/src/views/Conversations/Components/ConversationsList.jsx
@@ -116,6 +116,7 @@ const Conversations = () => {
letterItem={{id: item.whatsapp_name.trim() || item.whatsapp_phone_number, letter: item.whatsapp_name.trim() || item.whatsapp_phone_number}}
alt={`${item.whatsapp_name.trim()}`}
title={item.whatsapp_name.trim() || item.whatsapp_phone_number}
+ subtitle={item.whatsapp_phone_number}
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/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 (