回复引用

dev/chat
Lei OT 1 year ago
parent e3b5b90a9a
commit 271ed8e089

@ -90,6 +90,12 @@ export const receivedMessageList = (targetId, data) => ({
type: NAME_SPACE + 'RECEIVED_MESSAGE_LIST',
payload: { targetId, data },
})
export const setReplyTo = (data) => {
return {
type: NAME_SPACE + 'SET_REFERENCE_MSG',
payload: data,
};
};
export const fetchTemplates = async () => {
const data = await fetchJSON(`${API_HOST}/listtemplates`);

@ -25,14 +25,21 @@ export const replaceTemplateString = (str, replacements) => {
export const sentMsgTypeMapped = {
text: {
type: 'text',
contentToSend: (msg) => ({ action: 'message', actionId: msg.id, renderId: msg.id, to: msg.to, msgtype: 'text', msgcontent: { body: msg.text } }),
contentToRender: (msg) => ({ ...msg, actionId: msg.id, conversationid: msg.id.split('.')[0], }),
contentToSend: (msg) => ({
action: 'message',
actionId: msg.id,
renderId: msg.id,
to: msg.to,
msgtype: 'text',
msgcontent: { body: msg.text, ...(msg.context ? { context: msg.context } : {}) },
}),
contentToRender: (msg) => ({ ...msg, actionId: msg.id, conversationid: msg.id.split('.')[0], ...(msg.context ? { reply: { message: msg.context.message_origin.text, title: msg.context.message_origin.senderName || 'Reference' } } : {}) }),
},
whatsappTemplate: {
contentToSend: (msg) => ({ action: 'message', actionId: msg.id, renderId: msg.id, to: msg.to, msgtype: 'template', msgcontent: msg.template }),
contentToRender: (msg) => {
console.log(msg);
const templateDataMapped = msg.template?.components ? msg.template.components.reduce((r, v) => ({...r, [v.type]: v}), {}) : null;
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);
// const fillTemplate = templateParam.length ? replaceTemplateString(msg.template_origin.components.body?.[0]?.text || '', templateParam) : (msg.template_origin.components.body?.[0]?.text || '');
// const footer = msg.template_origin.components?.footer?.[0]?.text || '';
@ -167,7 +174,7 @@ export const whatsappMsgTypeMapped = {
unsupported: { type: 'system', data: (msg) => ({ text: 'Message type is currently not supported.' }) },
reaction: {
type: 'text',
data: (msg) => ({ id: msg.wamid, text: msg.reaction?.emoji || msg.reaction?.text?.body || 'Reaction', reply: { message: '{content}', title: 'React from' } }),
data: (msg) => ({ id: msg.wamid, text: msg.reaction?.emoji || msg.reaction?.text?.body || 'Reaction', reply: { message: '{content}', title: 'React from' } }), // todo:
},
document: {
type: 'file',
@ -198,6 +205,7 @@ export const parseRenderMessageItem = (msg) => {
...(typeof whatsappMsgTypeMapped[msg.type].type === 'function' ? whatsappMsgTypeMapped[msg.type].type(msg) : { type: whatsappMsgTypeMapped[msg.type].type || 'text' }),
// type: whatsappMsgTypeMapped?.[msg.type]?.type || 'text',
sender: msg.from,
senderName: msg?.customerProfile?.name || msg.from,
// status: msg?.status || 'waiting',
// title: msg.customerProfile.name,
// replyButton: true,
@ -221,9 +229,11 @@ export const parseRenderMessageList = (messages, conversationid = null) => {
...(typeof whatsappMsgTypeMapped[msgType].type === 'function' ? whatsappMsgTypeMapped[msgType].type(msg) : { type: whatsappMsgTypeMapped[msgType].type || 'text' }),
date: msgContent?.sendTime || msg.msgtime || '',
sender: msgContent.from,
senderName: msgContent?.customerProfile?.name || msgContent.from,
...(msg.msg_direction === 'outbound'
? {
sender: 'me',
senderName: 'me',
status: msgStatusRenderMapped[msgContent?.status || 'failed'],
dateString: msgStatusRenderMapped[msgContent?.status || 'failed'] === 'failed' ? '发送失败 ❌' : '',
}

@ -13,5 +13,6 @@ const initialState = {
activeConversations: {}, // 激活的对话的消息列表: { [conversationId]: [] }
currentConversation: {}, // 当前对话
referenceMsg: {},
};
export default initialState;

@ -126,6 +126,8 @@ const ConversationReducer = (state = initialState, action) => {
},
};
}
case NAME_SPACE + 'SET_REFERENCE_MSG':
return {...state, referenceMsg: action.payload};
case NAME_SPACE + 'ADD_ERROR': {
console.log('add error', state.errors, action.payload);
const prelist = state.errors || [];

@ -1,10 +1,10 @@
import React, { useState } from 'react';
import { Input, Flex, Button } from 'antd';
import { Input, Flex, Button, } from 'antd';
// import { Input } from 'react-chat-elements';
import { useConversationState, useConversationDispatch } from '@/stores/ConversationContext';
import { useAuthContext } from '@/stores/AuthContext';
import { sentNewMessage } from '@/actions/ConversationActions';
import { SendOutlined, MessageOutlined, SmileOutlined } from '@ant-design/icons';
import { sentNewMessage, setReplyTo } from '@/actions/ConversationActions';
import { SendOutlined, MessageOutlined, SmileOutlined, PictureOutlined, CommentOutlined, UploadOutlined, CloudUploadOutlined, FolderAddOutlined, FilePdfOutlined, CloseCircleOutlined } from '@ant-design/icons';
import { isEmpty } from '@/utils/utils';
import { v4 as uuid } from 'uuid';
import { sentMsgTypeMapped } from '@/lib/msgUtils';
@ -14,7 +14,7 @@ import dayjs from 'dayjs';
const InputBox = () => {
const { loginUser } = useAuthContext();
const { userId } = loginUser;
const { websocket, websocketOpened, currentConversation } = useConversationState();
const { websocket, websocketOpened, currentConversation, referenceMsg } = useConversationState();
const dispatch = useConversationDispatch();
const [textContent, setTextContent] = useState('');
@ -22,6 +22,8 @@ const InputBox = () => {
const gt24h = currentConversation.last_received_time ? dayjs().diff(dayjs(currentConversation.last_received_time), 'hour') > 24 : true;
const textabled = talkabled && !gt24h;
const invokeSendMessage = (msgObj) => {
console.log('sendMessage------------------', msgObj);
const contentToSend = sentMsgTypeMapped[msgObj.type].contentToSend(msgObj);
@ -42,19 +44,27 @@ const InputBox = () => {
id: `${currentConversation.sn}.${uuid()}`, // Date.now().toString(16),
date: new Date(),
status: 'waiting',
...(referenceMsg.id ? { context: { message_id: referenceMsg.id, message_origin: referenceMsg } } : {}),
};
invokeSendMessage(msgObj);
setTextContent('');
dispatch(setReplyTo({}));
}
};
return (
<div>
{referenceMsg.id && (
<Flex justify='space-between' className='reply-to bg-gray-100 p-1 rounded-none text-slate-500'>
<div className=' border-l-3 border-y-0 border-r-0 border-slate-300 border-solid pl-2 py-1'>{referenceMsg.text}</div>
<Button type='text' title='取消引用' className=' rounded-none text-slate-500' icon={<CloseCircleOutlined />} size={'middle'} onClick={() => dispatch(setReplyTo({}))} />
</Flex>
)}
<Input.TextArea
size='large'
placeholder={gt24h ? 'The session has expired, please send a template message to say hello' : 'Enter for Send, Shift+Enter for new line'}
placeholder={gt24h ? 'This session has expired. Please send a template message to activate the session' : 'Enter for Send, Shift+Enter for new line'}
rows={2}
disabled={!talkabled || gt24h}
disabled={!textabled}
value={textContent}
onChange={(e) => setTextContent(e.target.value)}
className='rounded-b-none'
@ -64,13 +74,19 @@ const InputBox = () => {
handleSendText();
}
}}
autoSize={{minRows: 2, maxRows: 6}}
autoSize={{ minRows: 2, maxRows: 6 }}
/>
<Flex justify={'space-between'} className=' bg-gray-200 p-1 rounded-b'>
<Flex gap={4} className='divide-y-0 divide-x divide-solid divide-gray-500 '>
<InputTemplate key='templates' disabled={!talkabled} invokeSendMessage={invokeSendMessage} />
{/* <Button type='text' className=' rounded-none text-primary' icon={<PictureOutlined />} size={'middle'} /> */}
{/* <Button type='text' className=' rounded-none text-primary' icon={<FolderAddOutlined />} size={'middle'} /> */}
{/* <Button type='text' className=' rounded-none text-primary' icon={<CloudUploadOutlined />} size={'middle'} /> */}
{/* <Button type='text' className=' rounded-none text-primary' icon={<FilePdfOutlined />} size={'middle'} /> */}
</Flex>
<Button key={'send-btn'} onClick={handleSendText} type='primary' size='middle' icon={<SendOutlined />} disabled={!talkabled || gt24h}>
<Button key={'send-btn'} onClick={handleSendText} type='primary' size='middle' icon={<SendOutlined />}
disabled={!textabled}
>
Send
</Button>
</Flex>

@ -1,10 +1,12 @@
import { useEffect, useState, useRef, useMemo } from 'react';
import { Image, Alert } from 'antd';
import { MessageBox } from 'react-chat-elements';
import { useConversationState } from '@/stores/ConversationContext';
import { useConversationState, useConversationDispatch } from '@/stores/ConversationContext';
import { setReplyTo } from '@/actions/ConversationActions';
const Messages = () => {
const { activeConversations, currentConversation } = useConversationState();
const dispatch = useConversationDispatch();
const messagesList = useMemo(() => activeConversations[currentConversation.sn] || [], [activeConversations, currentConversation.sn]);
console.log('messagesList----------------------------------------------------', messagesList);
@ -30,7 +32,6 @@ const Messages = () => {
return (
<div>
{messagesList.map((message, index) => (
<MessageBox
// className={message.sender === 'me' ? 'whatsappme-container' : ''}
@ -45,9 +46,16 @@ const Messages = () => {
? {
style: { backgroundColor: '#ccd5ae' },
notchStyle: { fill: '#ccd5ae' },
// forwarded: false,
// todo: reaction: emoji
replyButton: message.type === 'text' && message.status !== 'failed' ? true : false,
onReplyClick: () => dispatch(setReplyTo(message)),
className: 'whatsappme-container whitespace-pre-wrap',
}
: {})}
: {
replyButton: message.type === 'text' ? true : false,
onReplyClick: () => dispatch(setReplyTo(message)),
})}
// notchStyle={{fill: '#ccd5ae'}}
// copiableDate={false}
/>

Loading…
Cancel
Save