From e3aa4c25dd78b258a39acf358cbfd2bf35d91154 Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 27 Feb 2024 10:23:19 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E8=BF=9E;=20=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E7=94=A8=E6=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/msgUtils.js | 6 +- src/lib/realTimeAPI.js | 4 +- src/stores/ConversationStore.js | 8 ++- src/views/AuthApp.jsx | 2 +- .../Components/ConversationsList.jsx | 5 +- .../Components/Input/ImageUpload.jsx | 9 ++- .../Components/Input/Template.jsx | 5 +- .../Components/InputComposer.jsx | 56 +++++++++++++------ .../Components/MessagesHeader.jsx | 25 ++++++--- src/views/Conversations/Conversations.css | 8 +++ 10 files changed, 88 insertions(+), 40 deletions(-) diff --git a/src/lib/msgUtils.js b/src/lib/msgUtils.js index 85268dc..fa200dd 100644 --- a/src/lib/msgUtils.js +++ b/src/lib/msgUtils.js @@ -67,7 +67,7 @@ export const sentMsgTypeMapped = { to: msg.to, msgtype: 'image', msgcontent: { - image: { link: msg.data.uri }, + image: { link: msg.data.uri, caption: msg.text }, ...(msg.context ? { context: msg.context, message_origin: msg.message_origin } : {}), }, }), @@ -96,7 +96,7 @@ export const sentMsgTypeMapped = { conversationid: msg.id.split('.')[0], type: 'text', title: msg.template_origin.components.header?.[0]?.text || '', - text: templateDataMapped?.body?.text || '', // msg.template_origin.components.body?.[0]?.text || '', + text: autoLinkText( templateDataMapped?.body?.text || ''), // msg.template_origin.components.body?.[0]?.text || '', }; }, }, @@ -247,7 +247,7 @@ export const whatsappMsgTypeMapped = { type: 'text', data: (msg) => { const templateDataMapped = msg.template?.components ? msg.template.components.reduce((r, v) => ({...r, [v.type]: v}), {}) : null; - return { id: msg.wamid, text: templateDataMapped?.body?.text || templateDataMapped?.body?.parameters?.[0]?.text || '', title: msg.template.name } + return { id: msg.wamid, text: autoLinkText(templateDataMapped?.body?.text || templateDataMapped?.body?.parameters?.[0]?.text || ''), title: msg.template.name } }, renderForReply: (msg) => { const templateDataMapped = msg.template?.components ? msg.template.components.reduce((r, v) => ({ ...r, [v.type]: v }), {}) : null; diff --git a/src/lib/realTimeAPI.js b/src/lib/realTimeAPI.js index d055084..994e904 100644 --- a/src/lib/realTimeAPI.js +++ b/src/lib/realTimeAPI.js @@ -54,11 +54,11 @@ export class RealTimeAPI { return this.webSocket.pipe( // retry(10) retry({ - count: 10, + count: 20, // delay: 3000, delay: (errors, index) => { this.onRetry(index); - return timer(3000); + return timer(10000); }, resetOnSuccess: true, }), diff --git a/src/stores/ConversationStore.js b/src/stores/ConversationStore.js index dd3ce0f..aca1503 100644 --- a/src/stores/ConversationStore.js +++ b/src/stores/ConversationStore.js @@ -69,7 +69,7 @@ const websocketSlice = (set, get) => ({ }, disconnectWebsocket: () => { const { websocket } = get(); - websocket.disconnect(); + if (websocket) websocket.disconnect(); return set({ websocket: null }); }, reconnectWebsocket: (userId) => { @@ -125,6 +125,11 @@ const referenceMsgSlice = (set) => ({ setReferenceMsg: (referenceMsg) => set({ referenceMsg }), }); +const complexMsgSlice = (set) => ({ + complexMsg: {}, + setComplexMsg: (complexMsg) => set({ complexMsg }), +}); + const conversationSlice = (set, get) => ({ conversationsList: [], currentConversation: {}, @@ -257,6 +262,7 @@ export const useConversationStore = create(devtools((set, get) => ({ ...templatesSlice(set, get), ...messageSlice(set, get), ...referenceMsgSlice(set, get), + ...complexMsgSlice(set, get), // state actions addError: (error) => set((state) => ({ errors: [...state.errors, error] })), diff --git a/src/views/AuthApp.jsx b/src/views/AuthApp.jsx index d74d98a..bfcfd94 100644 --- a/src/views/AuthApp.jsx +++ b/src/views/AuthApp.jsx @@ -43,7 +43,7 @@ console.info(loginUser) useConversationStore.getState().fetchInitialData(userId); } return () => { - //useConversationStore.getState().disconnectWebsocket(); + useConversationStore.getState().disconnectWebsocket(); } }, []) diff --git a/src/views/Conversations/Components/ConversationsList.jsx b/src/views/Conversations/Components/ConversationsList.jsx index 74de17d..12ca846 100644 --- a/src/views/Conversations/Components/ConversationsList.jsx +++ b/src/views/Conversations/Components/ConversationsList.jsx @@ -112,12 +112,13 @@ const Conversations = () => { {...item} key={item.sn} id={item.sn} - avatar={`https://api.dicebear.com/7.x/avataaars/svg?seed=${item.whatsapp_name.trim() || item.whatsapp_phone_number}`} + // avatar={`https://api.dicebear.com/7.x/avataaars/svg?seed=${item.whatsapp_name.trim() || item.whatsapp_phone_number}`} + 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} date={item.last_received_time} unread={item.unread_msg_count} - className={String(item.sn) === String(currentConversation.sn) ? '__active text-primary underline bg-whatsapp-me border-y-0 border-e-0 border-s-2 border-solid border-whatsapp-me ' : ''} + className={String(item.sn) === String(currentConversation.sn) ? '__active text-primary border-y-0 border-e-0 border-s-2 border-solid border-whatsapp-me ' : ''} onClick={() => onSwitchConversation(item)} /> diff --git a/src/views/Conversations/Components/Input/ImageUpload.jsx b/src/views/Conversations/Components/Input/ImageUpload.jsx index 043493d..357ca40 100644 --- a/src/views/Conversations/Components/Input/ImageUpload.jsx +++ b/src/views/Conversations/Components/Input/ImageUpload.jsx @@ -14,6 +14,7 @@ import { CloseCircleOutlined, } from '@ant-design/icons'; import useConversationStore from '@/stores/ConversationStore'; +import { v4 as uuid } from 'uuid'; const props = { name: 'file', @@ -25,15 +26,17 @@ const props = { }; const ImageUpload = ({ disabled, invokeSendMessage }) => { - const { currentConversation, referenceMsg, setReferenceMsg } = useConversationStore(); + const { currentConversation, setComplexMsg } = useConversationStore(); const [uploading, setUploading] = useState(false); const handleSendImage = (src) => { const msgObj = { type: 'photo', data: { uri: src, }, + id: uuid(), }; - invokeSendMessage(msgObj); + setComplexMsg(msgObj); + // invokeSendMessage(msgObj); }; return ( { // 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'); + handleSendImage('https://images.chinahighlights.com//allpicture/2020/04/9330cd3c78a34c81afd3b1fb.jpg'); } else if (info.file.status === 'error') { message.error(`图片添加失败`); } diff --git a/src/views/Conversations/Components/Input/Template.jsx b/src/views/Conversations/Components/Input/Template.jsx index 6a83717..8db6f02 100644 --- a/src/views/Conversations/Components/Input/Template.jsx +++ b/src/views/Conversations/Components/Input/Template.jsx @@ -24,8 +24,7 @@ const splitTemplate = (template) => { const InputTemplate = ({ disabled = false, invokeSendMessage }) => { const searchInputRef = useRef(null); const { notification } = App.useApp(); - const { loadUser } = useAuthStore(); - const loginUser = loadUser(); + const { loginUser } = useAuthStore() const { currentConversation, templates } = useConversationStore(); // 用于替换变量: customer, agent const valueMapped = { ...cloneDeep(currentConversation), ...objectMapper(loginUser, { username: [{ key: 'agent_name' }, { key: 'your_name' }] }) }; @@ -58,7 +57,7 @@ const InputTemplate = ({ disabled = false, invokeSendMessage }) => { name: fromTemplate.name, language: { code: fromTemplate.language }, components: fromTemplate.components_origin.map((citem) => { - const keys = (citem?.text || '').match(/{{(.*?)}}/g).map((key) => key.replace(/{{|}}/g, '')); + const keys = ((citem?.text || '').match(/{{(.*?)}}/g) || []).map((key) => key.replace(/{{|}}/g, '')); const params = keys.map((v) => ({ type: 'text', text: getNestedValue(mergeInput, [v]) })); const paramText = params.map((p) => p.text); const fillTemplate = paramText.length ? replaceTemplateString(citem?.text || '', paramText) : citem?.text || ''; diff --git a/src/views/Conversations/Components/InputComposer.jsx b/src/views/Conversations/Components/InputComposer.jsx index 368828d..3cd1b08 100644 --- a/src/views/Conversations/Components/InputComposer.jsx +++ b/src/views/Conversations/Components/InputComposer.jsx @@ -1,9 +1,20 @@ import React, { useState } from 'react'; -import { Input, Flex, Button, } from 'antd'; +import { Input, Flex, Button, Image } from 'antd'; // import { Input } from 'react-chat-elements'; -import useAuthStore from '@/stores/AuthStore' +import useAuthStore from '@/stores/AuthStore'; import useConversationStore from '@/stores/ConversationStore'; -import { SendOutlined, MessageOutlined, SmileOutlined, PictureOutlined, CommentOutlined, UploadOutlined, CloudUploadOutlined, FolderAddOutlined, FilePdfOutlined, CloseCircleOutlined } from '@ant-design/icons'; +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'; @@ -12,17 +23,15 @@ import InputImageUpload from './Input/ImageUpload'; import dayjs from 'dayjs'; const InputBox = () => { - const { loadUser } = useAuthStore(); - const loginUser = loadUser(); - const { userId } = loginUser; - const { websocket, websocketOpened, currentConversation, referenceMsg, setReferenceMsg, sentOrReceivedNewMessage } = useConversationStore(); + const { loginUser: { userId } } = useAuthStore() + const { websocket, websocketOpened, currentConversation, referenceMsg, setReferenceMsg, complexMsg, setComplexMsg, sentOrReceivedNewMessage } = useConversationStore(); const [textContent, setTextContent] = useState(''); const talkabled = !isEmpty(currentConversation.sn) && websocketOpened; const gt24h = currentConversation.last_received_time ? dayjs().diff(dayjs(currentConversation.last_received_time), 'hour') > 24 : true; - const textabled = (talkabled && !gt24h); + const textabled = talkabled && !gt24h; const invokeSendMessage = (msgObj) => { const msgObjMerge = { @@ -44,13 +53,15 @@ const InputBox = () => { setTextContent(''); setReferenceMsg({}); + setComplexMsg({}); }; const handleSendText = () => { - if (textContent.trim() !== '') { + if (textContent.trim() !== '' || !isEmpty(complexMsg) ) { const msgObj = { type: 'text', text: textContent, + ...complexMsg, }; invokeSendMessage(msgObj); } @@ -61,8 +72,19 @@ const InputBox = () => { {referenceMsg.id && (
- {referenceMsg.senderName}{referenceMsg.originText}
-
diff --git a/src/views/Conversations/Components/MessagesHeader.jsx b/src/views/Conversations/Components/MessagesHeader.jsx index 33415c8..21597bd 100644 --- a/src/views/Conversations/Components/MessagesHeader.jsx +++ b/src/views/Conversations/Components/MessagesHeader.jsx @@ -1,18 +1,29 @@ -import { createContext, useContext, useEffect, useState } from 'react'; import useConversationStore from '@/stores/ConversationStore'; - -import { Flex, Typography, Avatar, Alert } from 'antd'; +import useAuthStore from '@/stores/AuthStore' +import { Flex, Typography, Avatar, Alert, Button, Tooltip } from 'antd'; +import { ReloadOutlined, ApiOutlined } from '@ant-design/icons'; import { LoadingOutlined } from '@ant-design/icons'; -import LocalTimeClock from './LocalTimeClock'; const MessagesHeader = () => { - const {websocketOpened, websocketRetrying, websocketRetrytimes, currentConversation} = useConversationStore(); + const { loginUser: { userId } } = useAuthStore() + const {websocketOpened, websocketRetrying, websocketRetrytimes, currentConversation, connectWebsocket} = useConversationStore(); return ( <> - {websocketOpened===false && } + {websocketOpened === false && ( + +