perf(前端): wai 二维码扫码;

dev/supplier-email-drawer
Lei OT 1 year ago
parent 34d7e06007
commit 7d0fbb96d1

@ -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) => {

@ -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] })),

@ -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 (
<div>
<CheckCircleFilled
style={{
color: '#52c41a',
}}
/>
<p>
<Button type='link' onClick={handelGernaterQR}>
<ReloadOutlined /> {info.locale?.refresh}
</Button>
</p>
</div>
)
case 'loading':
return (
<Space direction="vertical">
<Spin />
<p>Loading...</p>
</Space>
)
case 'scanned':
return (
<div>
<CheckCircleFilled
style={{
color: 'green',
}}
/>{' ' + loginUser.whatsAppNo}
{info.locale?.scanned}
</div>
)
default:
return null
}
}
return (
<>
<Row>
@ -224,7 +163,7 @@ function Profile() {
<Flex gap='middle' vertical justify={'center'} align={'center'}>
<Conditional
condition={isPermitted(PERM_USE_WHATSAPP)}
whenTrue={<QRCode size={264} value={qrCode} errorLevel='L' status={qrStatus} statusRender={customStatusRender} />}
whenTrue={<WAIQRCode />}
/>
</Flex>
</Col>

@ -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 (
<div>
<ExclamationCircleFilled className='text-orange-500' />
已过期
<p>
<Button type='link' onClick={handelGernaterQR}>
<ReloadOutlined /> {info.locale?.refresh}
</Button>
</p>
</div>
)
case 'loading':
return (
<Space direction='vertical'>
<Spin />
<p>Loading...</p>
</Space>
)
case 'scanned':
return (
<div>
<CheckCircleFilled
style={{
color: 'green',
}}
/>
{' ' + whatsAppNo}已登录
{/* {info.locale?.scanned} */}
</div>
)
default:
return null
}
}
return <QRCode size={264} value={qrCode} errorLevel='L' status={qrStatus} statusRender={customStatusRender} />
}
export default WAIQRCode
Loading…
Cancel
Save