From 7d0fbb96d1a0435e2cd88a6c6f590283a96fac1c Mon Sep 17 00:00:00 2001 From: Lei OT Date: Tue, 31 Dec 2024 16:49:13 +0800 Subject: [PATCH] =?UTF-8?q?perf(=E5=89=8D=E7=AB=AF):=20wai=20=E4=BA=8C?= =?UTF-8?q?=E7=BB=B4=E7=A0=81=E6=89=AB=E7=A0=81;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/channel/bubbleMsgUtils.js | 57 +++++++++++++------- src/stores/ConversationStore.js | 23 +++++++- src/views/accounts/Profile.jsx | 71 ++----------------------- src/views/accounts/WAIQRCode.jsx | 91 ++++++++++++++++++++++++++++++++ 4 files changed, 154 insertions(+), 88 deletions(-) create mode 100644 src/views/accounts/WAIQRCode.jsx diff --git a/src/channel/bubbleMsgUtils.js b/src/channel/bubbleMsgUtils.js index a8ccdd0..41150f5 100644 --- a/src/channel/bubbleMsgUtils.js +++ b/src/channel/bubbleMsgUtils.js @@ -285,19 +285,21 @@ const whatsappMsgMapped = { 'whatsapp.inbound_message.received': { getMsg: (result) => { // console.log('whatsapp.inbound_message.received', result); - const data1 = pick(result, ['conversationid', 'opi_sn', 'coli_sn', 'coli_id']); - return isEmpty(result?.whatsappInboundMessage) ? null : { ...result.whatsappInboundMessage, ...data1, messageorigin: result.messageorigin, msg_source: 'WABA', msg_direction: 'inbound' }; + const data1 = pick(result, ['conversationid', 'opi_sn', 'coli_sn', 'coli_id']) + return isEmpty(result?.whatsappInboundMessage) ? null : { ...result.whatsappInboundMessage, ...data1, messageorigin: result.messageorigin, msg_source: 'WABA', msg_direction: 'inbound' } }, contentToRender: (contentObj) => { // console.log('whatsapp.inbound_message.received to render', contentObj); - return parseRenderMessageItem(contentObj); + return parseRenderMessageItem(contentObj) }, contentToUpdate: () => null, }, 'whatsapp.message.updated': { getMsg: (result) => { // console.log('getMsg', result); - return isEmpty(result?.whatsappMessage) ? null : { ...result.whatsappMessage, conversationid: result.conversationid, messageorigin: result.messageorigin, msg_source: 'WABA', msg_direction: 'outbound' }; + return isEmpty(result?.whatsappMessage) + ? null + : { ...result.whatsappMessage, conversationid: result.conversationid, messageorigin: result.messageorigin, msg_source: 'WABA', msg_direction: 'outbound' } }, contentToRender: (contentObj) => { if (contentObj?.status === 'failed' && ['130472', 'BAD_REQUEST'].includes(contentObj.errorCode)) { @@ -307,28 +309,30 @@ const whatsappMsgMapped = { text: { body: `❌ ${whatsappError?.[contentObj.errorCode] || contentObj.errorMessage}` }, // contentObj.errorMessage // Message failed to send. id: contentObj.id, wamid: contentObj.id, - }; - return parseRenderMessageItem(contentObj); + } + return parseRenderMessageItem(contentObj) } // * 仅更新消息状态, 没有输出 - return null; + return null }, contentToUpdate: (msgcontent) => ({ ...msgcontent, ...parseRenderMessageItem(msgcontent), id: msgcontent.wamid, - status: msgStatusRenderMapped[(msgcontent?.status || 'failed')], + status: msgStatusRenderMapped[msgcontent?.status || 'failed'], sender: 'me', - dateString: msgcontent.status==='failed' ? `发送失败 ${whatsappError?.[msgcontent.errorCode] || msgcontent.errorMessage || ''} ❌` : '', + dateString: msgcontent.status === 'failed' ? `发送失败 ${whatsappError?.[msgcontent.errorCode] || msgcontent.errorMessage || ''} ❌` : '', }), }, 'wai.message.received': { getMsg: (result) => { - const data1 = pick(result, ['conversationid', 'opi_sn', 'coli_sn', 'coli_id']); - return isEmpty(result?.waiMessage) ? null : { ...result.waiMessage, ...data1, messageorigin: result.messageorigin, msg_source: 'wai', ...objectMapper(result.waiMessage, { direction: {key: 'msg_direction'} }) }; + const data1 = pick(result, ['conversationid', 'opi_sn', 'coli_sn', 'coli_id']) + return isEmpty(result?.waiMessage) + ? null + : { ...result.waiMessage, ...data1, messageorigin: result.messageorigin, msg_source: 'wai', ...objectMapper(result.waiMessage, { direction: { key: 'msg_direction' } }) } }, contentToRender: (contentObj) => { - return parseRenderMessageItem(contentObj); + return parseRenderMessageItem(contentObj) }, contentToUpdate: () => null, }, @@ -336,7 +340,13 @@ const whatsappMsgMapped = { getMsg: (result) => { return isEmpty(result?.waiMessage) ? null - : { ...result.waiMessage, conversationid: result.conversationid, messageorigin: result.messageorigin, msg_source: 'wai', ...objectMapper(result.waiMessage, { direction: {key: 'msg_direction'} }) } + : { + ...result.waiMessage, + conversationid: result.conversationid, + messageorigin: result.messageorigin, + msg_source: 'wai', + ...objectMapper(result.waiMessage, { direction: { key: 'msg_direction' } }), + } }, contentToRender: (contentObj) => { if (contentObj?.status === 'failed') { @@ -346,22 +356,29 @@ const whatsappMsgMapped = { text: { body: `❌` }, // contentObj.errorMessage // Message failed to send. id: contentObj.id, wamid: contentObj.id, - }; - return parseRenderMessageItem(contentObj); + } + return parseRenderMessageItem(contentObj) } // * 仅更新消息状态, 没有输出 - return null; + return null }, contentToUpdate: (msgcontent) => ({ ...msgcontent, ...parseRenderMessageItem(msgcontent), id: msgcontent.wamid, - status: msgStatusRenderMapped[(msgcontent?.status || 'failed')], - sender: msgcontent.msg_direction === 'outbound' ? 'me' : (msgcontent?.customerProfile ?.name || ''), - dateString: msgcontent.status==='failed' ? `发送失败 ❌` : '', + status: msgStatusRenderMapped[msgcontent?.status || 'failed'], + sender: msgcontent.msg_direction === 'outbound' ? 'me' : msgcontent?.customerProfile?.name || '', + dateString: msgcontent.status === 'failed' ? `发送失败 ❌` : '', }), }, -}; + 'wai.creds.update': { + getMsg: (result) => { + return isEmpty(result?.waiMessage) + ? {} + : { ...result.waiMessage, conversationid: result.conversationid, msg_source: 'wai', } + }, + }, +} const emailMsgMapped = { 'email.inbound.received': { getMsg: (result) => { diff --git a/src/stores/ConversationStore.js b/src/stores/ConversationStore.js index 6a98ea7..3170233 100644 --- a/src/stores/ConversationStore.js +++ b/src/stores/ConversationStore.js @@ -54,6 +54,9 @@ const initialConversationState = { msgListLoading: false, detailPopupOpen: false, + + wai: {}, + }; const globalNotifySlice = (set) => ({ @@ -64,6 +67,11 @@ const globalNotifySlice = (set) => ({ clearGlobalNotify: () => set(() => ({ globalNotify: [] })), }) +const waiSlice = (set) => ({ + wai: {}, + setWai: (wai) => set({ wai }), +}); + // 顾问的自定义标签 const tagsSlice = (set) => ({ tags: [], @@ -158,7 +166,7 @@ const websocketSlice = (set, get) => ({ logWebsocket(data, 'I'); // olog('websocket Messages ----', data); // console.log(data); - const { updateMessageItem, sentOrReceivedNewMessage, addGlobalNotify } = get(); + const { updateMessageItem, sentOrReceivedNewMessage, addGlobalNotify, setWai } = get(); const { errcode, errmsg, result } = data; if (!result) { @@ -169,7 +177,7 @@ const websocketSlice = (set, get) => ({ // addError('Error Connecting to Server'); resultType = 'error'; } - // console.log(resultType, 'result.type'); + // console.log(resultType, '----result.type'); const msgObj = receivedMsgTypeMapped[resultType].getMsg(result); const msgRender = receivedMsgTypeMapped[resultType].contentToRender(msgObj); const msgUpdate = receivedMsgTypeMapped[resultType].contentToUpdate(msgObj); @@ -194,6 +202,16 @@ const websocketSlice = (set, get) => ({ const msgNotify = receivedMsgTypeMapped[resultType].contentToNotify(msgObj); addGlobalNotify(msgNotify); } + // WhatsApp creds update + if ([ + 'wai.creds.update' + ].includes(resultType)) { + const _data = receivedMsgTypeMapped[resultType].getMsg(result); + setWai(_data) + setTimeout(() => { + setWai({}); // 60s 后清空 + }, 60_000); + } // console.log('handleMessage*******************'); }, }); @@ -529,6 +547,7 @@ export const useConversationStore = create( ...filterSlice(set, get), ...globalNotifySlice(set, get), ...emailSlice(set, get), + ...waiSlice(set, get), // state actions addError: (error) => set((state) => ({ errors: [...state.errors, error] })), diff --git a/src/views/accounts/Profile.jsx b/src/views/accounts/Profile.jsx index 1e15f3e..1e67b95 100644 --- a/src/views/accounts/Profile.jsx +++ b/src/views/accounts/Profile.jsx @@ -1,12 +1,12 @@ -import { useEffect, useCallback, useState } from 'react' -import { Row, Col, Space, Input, Descriptions, Avatar, Tag, Divider, List, App, Button, Flex, Select, Spin, Form, Typography, QRCode, Tooltip } from 'antd' -import { UserOutlined, InfoCircleOutlined, ReloadOutlined, CheckCircleFilled } from '@ant-design/icons' +import { useEffect, useCallback } from 'react' +import { Row, Col, Space, Input, Descriptions, Avatar, Tag, Divider, List, App, Button, Flex, Select, Form, Typography, Tooltip } from 'antd' +import { UserOutlined, InfoCircleOutlined } from '@ant-design/icons' import useAuthStore from '@/stores/AuthStore' import { Conditional } from '@/components/Conditional' import { PERM_USE_WHATSAPP } from '@/stores/AuthStore' -import { fetchQRCode } from '@/actions/WaiAction' import { usingStorage } from '@/utils/usingStorage'; import { WAI_SERVER_KEY } from '@/config'; +import WAIQRCode from './WAIQRCode'; function Profile() { @@ -18,9 +18,6 @@ function Profile() { state.loginUser, state.setWhatsAppProfile, state.isPermitted ]) - const [qrCode, setQRCode] = useState('expired') - const [qrStatus, setQRStatus] = useState('expired') - useEffect(() => { // 测试错误捕获: // throw new Error('💥 CABOOM 💥') @@ -47,64 +44,6 @@ function Profile() { }) }, []) - const handelGernaterQR = useCallback(() => { - const phone = loginUser.whatsAppNo - setQRStatus('loading') - fetchQRCode(phone) - .then(r => { - setQRCode(r.result.qr) - setQRStatus('active') - setTimeout(() => { - // 模拟扫码登录成功,实际要请求后端状态来确定 - setQRStatus('scanned') - }, 50000); - }) - .catch(ex => { - setQRStatus('expired') - console.error(ex) - }) - }, []) - - const customStatusRender = (info) => { - switch (info.status) { - case 'expired': - return ( -
- 已过期 -

- -

-
- ) - case 'loading': - return ( - - -

Loading...

-
- ) - case 'scanned': - return ( -
- {' ' + loginUser.whatsAppNo}已登录 - {info.locale?.scanned} -
- ) - default: - return null - } - } - return ( <> @@ -224,7 +163,7 @@ function Profile() { } + whenTrue={} /> diff --git a/src/views/accounts/WAIQRCode.jsx b/src/views/accounts/WAIQRCode.jsx new file mode 100644 index 0000000..685353b --- /dev/null +++ b/src/views/accounts/WAIQRCode.jsx @@ -0,0 +1,91 @@ +import { useEffect, useCallback, useState } from 'react' +import { Space, App, Button, Spin, QRCode } from 'antd' +import { ReloadOutlined, CheckCircleFilled, ExclamationCircleFilled } from '@ant-design/icons' +import useAuthStore from '@/stores/AuthStore' +import { fetchQRCode } from '@/actions/WaiAction' +import useConversationStore from '@/stores/ConversationStore' +import { isEmpty } from '@/utils/commons' + +const connectionStateMappedQRCodeState = { + open: 'scanned', + offline: 'expired', +} + +const WAIQRCode = ({ ...props }) => { + const { notification, message } = App.useApp() + const { whatsAppNo } = useAuthStore((state) => state.loginUser) + + const [qrCode, setQRCode] = useState('expired') + const [qrStatus, setQRStatus] = useState('expired') // expired, loading, scanned + const handelGernaterQR = useCallback(() => { + setQRStatus('loading') + fetchQRCode(whatsAppNo) + .then((r) => { + // setQRCode(r.result.qr) + // setQRStatus('active') + // setTimeout(() => { + // // 模拟扫码登录成功,实际要请求后端状态来确定 + // setQRStatus('scanned') + // }, 3000); + }) + .catch((ex) => { + setQRStatus('expired') + console.error(ex) + }) + }, []) + + const [wai] = useConversationStore((state) => [state.wai]) + useEffect(() => { + if (isEmpty(wai)) { + setQRCode('expired') + setQRStatus('expired') + return () => {} + } + if (wai.qr) { + setQRCode(wai.qr) + } + setQRStatus(wai.status === '' ? 'active' : connectionStateMappedQRCodeState[wai.status]) + + return () => {} + }, [wai]) + const customStatusRender = (info) => { + switch (info.status) { + case 'expired': + return ( +
+ + 已过期 +

+ +

+
+ ) + case 'loading': + return ( + + +

Loading...

+
+ ) + case 'scanned': + return ( +
+ + {' ' + whatsAppNo}已登录 + {/* {info.locale?.scanned} */} +
+ ) + default: + return null + } + } + + return +} +export default WAIQRCode