feat: 解析Email气泡; 获取详情; 回复

2.0/email-builder
Lei OT 11 months ago
parent fbbe573768
commit 7a4a8df4a0

@ -512,6 +512,9 @@ export const whatsappMsgTypeMapped = {
return { id: msg.wamid, message: templateDataMapped?.body?.text || templateDataMapped?.body?.parameters?.[0]?.text || '', title: `${msg.template.name}` }; return { id: msg.wamid, message: templateDataMapped?.body?.text || templateDataMapped?.body?.parameters?.[0]?.text || '', title: `${msg.template.name}` };
}, },
}, },
email: {
data: (msg) => ({ id: msg.id, subject: msg.email.subject, originText: msg.text.body, title: msg?.customerProfile?.name || '' }),
},
}; };
/** /**
* render received msg * render received msg
@ -534,7 +537,7 @@ export const parseRenderMessageItem = (msg) => {
customer_name: msg?.customerProfile?.name || '', customer_name: msg?.customerProfile?.name || '',
whatsapp_name: msg?.customerProfile?.name || '', whatsapp_name: msg?.customerProfile?.name || '',
whatsapp_phone_number: isEmpty(msg?.customerProfile) ? msg.to : msg.from, whatsapp_phone_number: isEmpty(msg?.customerProfile) ? msg.to : msg.from,
whatsapp_msg_type: msg.type, whatsapp_msg_type: msg.msg_source==='WABA' ? msg.type : '',
statusCN: msgStatusRenderMappedCN[msg?.status || 'failed'], statusCN: msgStatusRenderMappedCN[msg?.status || 'failed'],
statusTitle: msgStatusRenderMappedCN[msg?.status || 'failed'], statusTitle: msgStatusRenderMappedCN[msg?.status || 'failed'],
replyButton: ['text', 'document', 'image'].includes(msg.type) && (msg?.status || '') !== 'failed', replyButton: ['text', 'document', 'image'].includes(msg.type) && (msg?.status || '') !== 'failed',
@ -562,17 +565,19 @@ export const parseRenderMessageItem = (msg) => {
export const parseRenderMessageList = (messages) => { export const parseRenderMessageList = (messages) => {
return messages.map((msg, i) => { return messages.map((msg, i) => {
let msgContentString = ''; let msgContentString = '';
if (typeof msg.msgtext_AsJOSN === 'string') { const msgtext = msg?.msgtext || msg?.msgtext_AsJOSN || {};
const messageorigin = msg?.messageorigin || msg?.messageorigin_AsJOSN || {};
if (typeof msgtext === 'string') {
// debug: json 缺少一部分 // debug: json 缺少一部分
msgContentString = msg.msgtext_AsJOSN.charAt(msg.msgtext_AsJOSN.length - 1) !== '}' ? msg.msgtext_AsJOSN + '}}' : msg.msgtext_AsJOSN; msgContentString = msgtext.charAt(msgtext.length - 1) !== '}' ? msgtext + '}}' : msgtext;
// if (msg.msgtext_AsJOSN.charAt(msg.msgtext_AsJOSN.length - 1) === '"') { // if (msg.msgtext.charAt(msg.msgtext.length - 1) === '"') {
// msgContentString = msg.msgtext_AsJOSN + '}}'; // msgContentString = msg.msgtext + '}}';
// } else { // } else {
// msgContentString = msg.msgtext_AsJOSN + '"}'; // msgContentString = msg.msgtext + '"}';
// } // }
} }
const msgContent = typeof msg.msgtext_AsJOSN === 'string' ? JSON.parse(msgContentString) : (msg.msgtext_AsJOSN || {}); const msgContent = typeof msgtext === 'string' ? JSON.parse(msgContentString) : (msgtext || {});
msgContent.template = msg.msgtype === 'template' ? { ...msgContent.template, ...msg.template_AsJOSN } : {}; msgContent.template = msg.msgtype === 'template' ? { ...msgContent.template, ...msg.template } : {};
const msgType = Object.keys(whatsappMsgTypeMapped).includes(msgContent.type) ? msgContent.type : 'unsupported'; const msgType = Object.keys(whatsappMsgTypeMapped).includes(msgContent.type) ? msgContent.type : 'unsupported';
// const parseMethod = msgContent.bizType === 'whatsapp' ? cloneDeep(whatsappMsgTypeMapped) : {}; // const parseMethod = msgContent.bizType === 'whatsapp' ? cloneDeep(whatsappMsgTypeMapped) : {};
let waCode, waError = ''; let waCode, waError = '';
@ -610,26 +615,26 @@ export const parseRenderMessageList = (messages) => {
statusTitle: msgStatusRenderMappedCN[msgContent?.status || 'failed'], statusTitle: msgStatusRenderMappedCN[msgContent?.status || 'failed'],
} }
: {}), : {}),
...((isEmpty(msg.messageorigin_AsJOSN) && (isEmpty(msgContent.context) || msgContent.context?.forwarded === true)) ...((isEmpty(messageorigin) && (isEmpty(msgContent.context) || msgContent.context?.forwarded === true))
// ...((isEmpty(msg.context) && isEmpty(msg.reaction)) || msg.context?.forwarded === true || isEmpty(msg.messageorigin) // ...((isEmpty(msg.context) && isEmpty(msg.reaction)) || msg.context?.forwarded === true || isEmpty(messageorigin)
// ...((isEmpty(msg.messageorigin_AsJOSN) || isEmpty(msgContent.context)) // ...((isEmpty(messageorigin) || isEmpty(msgContent.context))
? {} ? {}
: { : {
reply: { reply: {
message: msg.messageorigin_AsJOSN?.text?.body || msg.messageorigin_AsJOSN?.text, message: messageorigin?.text?.body || messageorigin?.text,
title: msg.messageorigin_AsJOSN?.customerProfile?.name || msg.messageorigin_AsJOSN?.senderName || 'me', title: messageorigin?.customerProfile?.name || messageorigin?.senderName || 'me',
...(typeof whatsappMsgTypeMapped[(msg.messageorigin_AsJOSN?.type || 'unsupported')]?.renderForReply === 'function' ...(typeof whatsappMsgTypeMapped[(messageorigin?.type || 'unsupported')]?.renderForReply === 'function'
? whatsappMsgTypeMapped[(msg.messageorigin_AsJOSN?.type || 'unsupported')].renderForReply(msg.messageorigin_AsJOSN) ? whatsappMsgTypeMapped[(messageorigin?.type || 'unsupported')].renderForReply(messageorigin)
: {}), : {}),
titleColor: msg.messageorigin_AsJOSN?.customerProfile?.name ? '#a791ff' : "#128c7e", titleColor: messageorigin?.customerProfile?.name ? '#a791ff' : "#128c7e",
// titleColor: msg.messageorigin_direction === 'inbound' ? '#a791ff' : "#128c7e", // titleColor: msg.messageorigin_direction === 'inbound' ? '#a791ff' : "#128c7e",
id: msgContent.context?.id || msgContent.context?.message_id || msgContent.reaction?.message_id, id: msgContent.context?.id || msgContent.context?.message_id || msgContent.reaction?.message_id,
}, },
origin: msg.messageorigin_AsJOSN, origin: messageorigin,
}), }),
// conversationid: conversationid, // conversationid: conversationid,
// title: msg.customerProfile.name, // title: msg.customerProfile.name,
whatsapp_msg_type: msgContent.type, whatsapp_msg_type: msg.msg_source==='WABA' ? msgContent.type : '',
}; };
}); });
}; };

@ -250,7 +250,7 @@ const MessagesList = ({ ...props }) => {
onTitleClick={() => handlePreview(message)} onTitleClick={() => handlePreview(message)}
notch={false} notch={false}
title={message.whatsapp_msg_type === 'text' ? '' : message.title} title={message.whatsapp_msg_type === 'text' ? '' : message.title}
text={<RenderText str={message?.text || ''} className={message.status === 'failed' ? 'line-through text-neutral-400' : ''} template={message.template_AsJOSN} />} text={<RenderText str={message?.text || ''} className={message.status === 'failed' ? 'line-through text-neutral-400' : ''} template={message.template} />}
copiableDate={true} copiableDate={true}
dateString={message.dateString || message.localDate} dateString={message.dateString || message.localDate}
className={[ className={[

@ -9,11 +9,11 @@ const ChatboxEmail = ({ onOpenEditor, onOpenEmail, ...message }) => {
const RenderText = memo(function renderText({ className, email, sender }) { const RenderText = memo(function renderText({ className, email, sender }) {
return ( return (
<div onClick={() => handlePreview(message)} className={`text-sm leading-5 emoji-text whitespace-pre-wrap cursor-pointer ${className}`} key={'msg-text'}> <div onClick={() => handlePreview(message)} className={`text-sm leading-5 emoji-text whitespace-pre-wrap cursor-pointer ${className}`} key={'msg-text'}>
{sender === 'me' && <div><b>From: </b>{email.fromName}&nbsp;&lt;{email.fromEmail}&gt;</div>} {sender === 'me' && <div><b>From: </b>{email.from}</div>}
<div><b>To: </b>{email.toName}&nbsp;&lt;{email.toEmail}&gt;</div> <div><b>To: </b>{email.to}</div>
<div ><b>Subject: </b>{email.subject}</div> <div ><b>Subject: </b>{email.email.subject}</div>
<hr className='border-0 border-solid border-b border-neutral-400'/> <hr className='border-0 border-solid border-b border-neutral-400'/>
<div className='line-clamp-2 text-neutral-600'>{email.abstract}</div> <div className='line-clamp-2 text-neutral-600'>{email.email.abstract}</div>
</div> </div>
); );
}); });
@ -26,7 +26,7 @@ const ChatboxEmail = ({ onOpenEditor, onOpenEmail, ...message }) => {
return ( return (
<MessageBox <MessageBox
{...message} {...message}
key={`${message.sn}.${message.id}`} key={`${message.sn}.${message.msgtext.id}`}
type='text' type='text'
title={ message.sender !== 'me' && title={ message.sender !== 'me' &&
<> <>
@ -34,7 +34,8 @@ const ChatboxEmail = ({ onOpenEditor, onOpenEmail, ...message }) => {
<span className={`pl-2 ${message.sender === 'me' ? '' : 'text-indigo-600'}`}> <span className={`pl-2 ${message.sender === 'me' ? '' : 'text-indigo-600'}`}>
<b>From: </b> <b>From: </b>
<span> <span>
{message?.emailOrigin?.fromName}&nbsp;&lt;{message?.emailOrigin.fromEmail}&gt; {/* {message?.emailOrigin?.fromName}&nbsp;&lt;{message?.emailOrigin.fromEmail}&gt; */}
{message.msgtext?.from}
</span> </span>
</span> </span>
</> </>
@ -42,11 +43,11 @@ const ChatboxEmail = ({ onOpenEditor, onOpenEmail, ...message }) => {
// titleColor={message.sender !== 'me' ? '#4f46e5' : ''} // 600 // titleColor={message.sender !== 'me' ? '#4f46e5' : ''} // 600
notch={false} notch={false}
position={message.sender === 'me' ? 'right' : 'left'} position={message.sender === 'me' ? 'right' : 'left'}
onReplyClick={() => onOpenEditor(message.emailOrigin)} onReplyClick={() => onOpenEditor(message.msgtext)}
// onReplyMessageClick={() => scrollToMessage(message.reply.id)} // onReplyMessageClick={() => scrollToMessage(message.reply.id)}
onOpen={() => handlePreview(message)} onOpen={() => handlePreview(message)}
onTitleClick={() => handlePreview(message)} onTitleClick={() => handlePreview(message)}
text={<RenderText str={message?.text || ''} className={message.status === 'failed' ? 'line-through text-neutral-400' : ''} email={message.emailOrigin} sender={message.sender} />} text={<RenderText str={message?.msgtext || ''} className={message.status === 'failed' ? 'line-through text-neutral-400' : ''} email={message.msgtext} sender={message.sender} />}
// forwarded={true} // forwarded={true}
// replyButton={message.sender !== 'me'} // replyButton={message.sender !== 'me'}
// replyButton={['text', 'document', 'image'].includes(message.whatsapp_msg_type)} // replyButton={['text', 'document', 'image'].includes(message.whatsapp_msg_type)}

@ -38,11 +38,11 @@ const EmailDetail = ({ open, setOpen, emailMsg, ...props }) => {
const [openEmailEditor, setOpenEmailEditor] = useState(false) const [openEmailEditor, setOpenEmailEditor] = useState(false)
const [fromEmail, setFromEmail] = useState('') const [fromEmail, setFromEmail] = useState('')
const [ReferEmailMsg, setReferEmailMsg] = useState('') const [ReferEmailMsg, setReferEmailMsg] = useState('')
const onOpenEditor = (emailOrigin, action) => { const onOpenEditor = (magtext, action) => {
const { replyToEmail: email_addr, content } = emailOrigin const { from } = magtext
setOpenEmailEditor(true) setOpenEmailEditor(true)
setFromEmail(email_addr) setFromEmail(from)
setReferEmailMsg(emailOrigin) setReferEmailMsg(magtext)
setAction(action) setAction(action)
setOpen(false) setOpen(false)
} }
@ -66,14 +66,14 @@ const EmailDetail = ({ open, setOpen, emailMsg, ...props }) => {
const ActionBtns = (props) => ( const ActionBtns = (props) => (
<div className={`flex items-center w-full ${props.className || ''}`}> <div className={`flex items-center w-full ${props.className || ''}`}>
<Button <Button
onClick={() => onOpenEditor(emailMsg, 'reply')} onClick={() => onOpenEditor(emailMsg.msgtext, 'reply')}
size='small' size='small'
type='text' type='text'
icon={<ReplyIcon className='text-indigo-500' />}> icon={<ReplyIcon className='text-indigo-500' />}>
回复 回复
</Button> </Button>
<Button <Button
onClick={() => onOpenEditor(emailMsg, 'forward')} onClick={() => onOpenEditor(emailMsg.msgtext, 'forward')}
size='small' size='small'
type='text' type='text'
icon={<ShareForwardIcon className='text-primary' />}> icon={<ShareForwardIcon className='text-primary' />}>
@ -87,14 +87,14 @@ const EmailDetail = ({ open, setOpen, emailMsg, ...props }) => {
<DnDModal <DnDModal
open={open} open={open}
setOpen={setOpen} setOpen={setOpen}
title={mailData.info?.subject} title={mailData.info?.subject || emailMsg?.msgtext?.email?.subject}
initial={{ top: 74 }} initial={{ top: 74 }}
onMove={onHandleMove} onMove={onHandleMove}
onResize={onHandleResize} onResize={onHandleResize}
footer={mobile ? <ActionBtns className='w-full' /> : null}> footer={mobile ? <ActionBtns className='w-full' /> : null}>
<div className='email-container flex flex-col gap-2 *:p-2 *:rounded-sm *:border-b *:border-gray-200 *:shadow-1md'> <div className='email-container flex flex-col gap-2 *:p-2 *:rounded-sm *:border-b *:border-gray-200 *:shadow-1md'>
<div className=' font-bold'>{mailData.info?.subject}</div> <div className=' font-bold'>{mailData.info?.subject || emailMsg?.msgtext?.email?.subject}</div>
<div> <div>
<div <div

@ -348,12 +348,12 @@ const MessageListFilter = ({ ...props }) => {
dataSource={data} dataSource={data}
loadMore={loadMore} loadMore={loadMore}
loading={loading} loading={loading}
renderItem={({ emailOrigin, ...item }) => ( renderItem={({ msgtext, ...item }) => (
<List.Item <List.Item
// actions={[item.localDate]} // actions={[item.localDate]}
className='cursor-pointer' className='cursor-pointer'
onClick={() => { onClick={() => {
onOpenEmail({ emailOrigin, ...item }); onOpenEmail({ msgtext, ...item });
setOpenPopup(false); setOpenPopup(false);
}}> }}>
<List.Item.Meta <List.Item.Meta
@ -363,20 +363,20 @@ const MessageListFilter = ({ ...props }) => {
// {item.senderName.substring(0, 3)} // {item.senderName.substring(0, 3)}
// </Avatar> // </Avatar>
} }
title={emailOrigin.subject} title={msgtext?.email?.subject}
// description={`To: ${emailOrigin.toEmail}`} // description={`To: ${msgtext.to}`}
description={ description={
<Flex justify={'space-between'} className='max-w-full overflow-hidden'> <Flex justify={'space-between'} className='max-w-full overflow-hidden'>
<div className='flex-auto line-clamp-1 break-all pr-2'>{`To: ${emailOrigin.toEmail}`}</div> <div className='flex-auto line-clamp-1 break-all pr-2'>{`To: ${msgtext.to}`}</div>
<div className=' basis-32 flex-grow-0 flex-shrink-0'>{item.localDate}</div> <div className=' basis-32 flex-grow-0 flex-shrink-0'>{item.localDate}</div>
</Flex> </Flex>
} }
/> />
{emailOrigin.abstract} {msgtext?.email?.abstract}
</List.Item> </List.Item>
)} )}
/> />
<EmailDetail open={openEmailDetail} setOpen={setOpenEmailDetail} emailDetail={emailDetail} key={`email-detail-1-${emailDetail.id}`} /> <EmailDetail open={openEmailDetail} setOpen={setOpenEmailDetail} emailMsg={emailDetail} key={`email-detail-1-${emailDetail.id}`} />
</> </>
); );
}; };

@ -58,54 +58,26 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, reference, quote = {}, ini
return () => {}; return () => {};
} }
setShowCc(!isEmpty(quote.cc)); setShowCc(!isEmpty(quote.cc));
const { fromEmail, replyToEmail, subject, content } = quote; const { from, email: { subject, content, mai_sn } } = quote;
// const preQuoteBody = `<p class="editor-paragraph" dir="ltr"><br></p><p class="editor-paragraph" dir="ltr"><br>
// <hr>
// <p class="editor-paragraph" dir="ltr">
// <b>
// <strong class="editor-text-bold" style="white-space: pre-wrap;">From: </strong>
// </b>
// <span style="white-space: pre-wrap;">${quote.fromName} &lt;${quote.fromEmail}&gt;</span>
// </p>
// <p class="editor-paragraph" dir="ltr">
// <b>
// <strong class="editor-text-bold" style="white-space: pre-wrap;">Sent: </strong>
// </b>
// <span style="white-space: pre-wrap;">${quote.sent}</span>
// </p>
// <p class="editor-paragraph" dir="ltr">
// <b>
// <strong class="editor-text-bold" style="white-space: pre-wrap;">To: </strong>
// </b>
// <span style="white-space: pre-wrap;">${quote.toName} &lt;${quote.toEmail}&gt;</span>
// </p>
// <p class="editor-paragraph" dir="ltr">
// <b>
// <strong class="editor-text-bold" style="white-space: pre-wrap;">Subject: </strong>
// </b>
// <span style="white-space: pre-wrap;">${subject}</span>
// </p>
// <p class="editor-paragraph" dir="ltr">${content}</p>
// `;
const preQuoteBody = `<br><br> const preQuoteBody = `<br><br>
<hr> <hr>
<p> <p>
<b> <b>
<strong >From: </strong> <strong >From: </strong>
</b> </b>
<span >${quote.fromName} &lt;${quote.fromEmail}&gt;</span> <span >${quote.from} </span>
</p> </p>
<p> <p>
<b> <b>
<strong >Sent: </strong> <strong >Sent: </strong>
</b> </b>
<span >${quote.sent}</span> <span >${quote.sendTime}</span>
</p> </p>
<p> <p>
<b> <b>
<strong >To: </strong> <strong >To: </strong>
</b> </b>
<span >${quote.toName} &lt;${quote.toEmail}&gt;</span> <span >${quote.to}</span>
</p> </p>
<p> <p>
<b> <b>
@ -117,12 +89,10 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, reference, quote = {}, ini
${content} ${content}
</p> </p>
`; `;
// <blockquote class="editor-quote">
// </blockquote>
setInitialContent(preQuoteBody); setInitialContent(preQuoteBody);
const _formValues = { const _formValues = {
to: replyToEmail || fromEmail, to: from || fromEmail,
cc: quote.cc || '', cc: quote.cc || '',
bcc: quote.bcc || '', bcc: quote.bcc || '',
subject: `Re: ${subject}`, subject: `Re: ${subject}`,
@ -252,7 +222,7 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, reference, quote = {}, ini
onCancel={() => { onCancel={() => {
form.resetFields(); form.resetFields();
}} }}
title={initialForm.subject || `${reference ? '回复: ' : '写邮件: '} ${fromEmail || ''}`} title={initialForm.subject || `${isEmpty(quote) ? '回复: ' : '写邮件: '} ${fromEmail || ''}`}
footer={ footer={
<div className='w-full flex gap-6 justify-start items-center text-indigo-600'> <div className='w-full flex gap-6 justify-start items-center text-indigo-600'>
<Button type='primary' onClick={onHandleSend}> <Button type='primary' onClick={onHandleSend}>
@ -289,17 +259,7 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, reference, quote = {}, ini
> >
<Form.Item label='收件人' className='w-full'> <Form.Item label='收件人' className='w-full'>
<Space.Compact className='w-full'> <Space.Compact className='w-full'>
{/* <Flex justify='space-between' className='w-full'> */} <Form.Item name={'to'} rules={[{ required: true }]} className='!flex-1'>
{/* <Mentions // todo: 用客人列表
split='; '
options={[
{ value: 'lyt@hainatravel.com', label: 'OT' },
{ value: 'zombieJ', label: 'zombieJ' },
{ value: 'yesmeck', label: 'yesmeck' },
]}
placeholder='@'
/> */}
<Form.Item name={'to'} rules={[{ required: true }]} className='flex-1'>
<Input className='w-full' /> <Input className='w-full' />
</Form.Item> </Form.Item>
<Flex gap={4}> <Flex gap={4}>
@ -314,7 +274,6 @@ const EmailEditorPopup = ({ open, setOpen, fromEmail, reference, quote = {}, ini
</Button> </Button>
)} )}
</Flex> </Flex>
{/* </Flex> */}
</Space.Compact> </Space.Compact>
{/* <Input {/* <Input
addonAfter={ addonAfter={

@ -67,7 +67,7 @@ const MessagesWrapper = ({ updateRead = true, forceGetMessages }) => {
// test: // test:
data.push(emailItem); data.push(emailItem);
data.push(emailReItem); // data.push(emailReItem);
setMsgLoading(false); setMsgLoading(false);
receivedMessageList(item.sn, data); receivedMessageList(item.sn, data);
@ -140,18 +140,20 @@ const MessagesWrapper = ({ updateRead = true, forceGetMessages }) => {
const [openEmailEditor, setOpenEmailEditor] = useState(false); const [openEmailEditor, setOpenEmailEditor] = useState(false);
const [fromEmail, setFromEmail] = useState(''); const [fromEmail, setFromEmail] = useState('');
const [ReferEmailMsg, setReferEmailMsg] = useState(''); const [ReferEmailMsg, setReferEmailMsg] = useState('');
const onOpenEditor = (emailOrigin) => { const onOpenEditor = (emailMsgContent) => {
const { replyToEmail: email_addr, content } = emailOrigin; const { from } = emailMsgContent; // msgtext
console.log('emailMsgContent', emailMsgContent);
setOpenEmailEditor(true); setOpenEmailEditor(true);
setFromEmail(email_addr); setFromEmail(from);
setReferEmailMsg(emailOrigin); setReferEmailMsg(emailMsgContent);
}; };
const [openEmailDetail, setOpenEmailDetail] = useState(false); const [openEmailDetail, setOpenEmailDetail] = useState(false);
const [emailDetail, setEmailDetail] = useState({}); const [emailDetail, setEmailDetail] = useState({});
const onOpenEmail = (email_detail) => { const onOpenEmail = (emailMsg) => {
setOpenEmailDetail(true); setOpenEmailDetail(true);
setEmailDetail(email_detail); setEmailDetail(emailMsg);
} }
return ( return (

Loading…
Cancel
Save