From 32076e94412648969af0905711332ef79db6454c Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 4 Mar 2024 11:38:07 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E6=B6=88=E6=81=AF=E6=8F=90=E9=86=92?= =?UTF-8?q?=E5=92=8C=E9=97=AA=E7=83=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/msgUtils.js | 32 ++++++++++ src/stores/ConversationStore.js | 107 ++++++++++++++++---------------- src/views/AuthApp.jsx | 35 +++-------- 3 files changed, 94 insertions(+), 80 deletions(-) diff --git a/src/lib/msgUtils.js b/src/lib/msgUtils.js index e7715e8..fae2c67 100644 --- a/src/lib/msgUtils.js +++ b/src/lib/msgUtils.js @@ -370,3 +370,35 @@ export const whatsappError = { '131048': '[131048] 账户被风控.', // 消息发送太多, 达到垃圾数量限制 '131031': '[131031] 账户已锁定.', }; + +/** + * 系统弹窗通知 + */ +export const handleNotification = (title, _options) => { + const options = { + requireInteraction: true, // 设置手动关闭 + tag: 'global-sales-notification', // 通知ID,同类通知建议设置相同ID,避免通知过多遮挡桌面 + // icon: '/favicon.ico', // 通知图标 + ..._options, + }; + + // 检查用户是否同意接受通知 + if (Notification.permission === 'granted') { + var notification = new Notification(title, options); + } else if (Notification.permission !== 'denied') { + Notification.requestPermission().then(function (permission) { + if (permission === 'granted') { + var notification = new Notification(title, options); + } + }); + } else { + // 用户拒绝通知权限 + } + + // 通知弹窗被点击后的回调 + notification.onclick = () => { + // window.parent.parent.focus(); + window.focus(); // 显示当前标签页 + notification.close(); // 关闭通知,适用于设置了手动关闭的通知 + }; +}; diff --git a/src/stores/ConversationStore.js b/src/stores/ConversationStore.js index 5fd1855..52e668d 100644 --- a/src/stores/ConversationStore.js +++ b/src/stores/ConversationStore.js @@ -1,7 +1,7 @@ import { create } from 'zustand'; import { RealTimeAPI } from '@/lib/realTimeAPI'; import { olog, isEmpty } from '@/utils/utils'; -import { receivedMsgTypeMapped } from '@/lib/msgUtils'; +import { receivedMsgTypeMapped, handleNotification } from '@/lib/msgUtils'; import { fetchConversationsList, fetchTemplates } from '@/actions/ConversationActions'; import { devtools } from 'zustand/middleware'; import { WS_URL } from '@/config'; @@ -57,7 +57,7 @@ const websocketSlice = (set, get) => ({ setWebsocketRetrytimes(0); }, () => { - setWebsocketOpened(false) + setWebsocketOpened(false); const newMsgList = Object.keys(activeConversations).reduce((acc, key) => { const newMsgList = activeConversations[key].slice(-10); acc[key] = newMsgList; @@ -72,7 +72,7 @@ const websocketSlice = (set, get) => ({ realtimeAPI.onMessage(handleMessage); realtimeAPI.onCompletion(() => addError('Connection broken')); - olog('Connecting to websocket...', realtimeAPI) + olog('Connecting to websocket...', realtimeAPI); setWebsocket(realtimeAPI); }, disconnectWebsocket: () => { @@ -81,7 +81,7 @@ const websocketSlice = (set, get) => ({ return set({ websocket: null }); }, reconnectWebsocket: (userId) => { - const {disconnectWebsocket, connectWebsocket} = get(); + const { disconnectWebsocket, connectWebsocket } = get(); disconnectWebsocket(); setTimeout(() => { connectWebsocket(userId); @@ -112,18 +112,9 @@ const websocketSlice = (set, get) => ({ } if (!isEmpty(msgRender)) { sentOrReceivedNewMessage(msgRender.conversationid, msgRender); - window.Notification.requestPermission().then(function (permission) { - if (permission === 'granted') { - const notification = new Notification(`${msgRender.senderName}`, { - body: msgRender?.text || `[ ${msgRender.type} ]`, - requireInteraction: true, // 设置手动关闭 - tag: 'global-sales-notification', // 通知ID,同类通知建议设置相同ID,避免通知过多遮挡桌面 - ...(msgRender.type === 'photo' ? { image: msgRender.data.uri } : {}), - }); - notification.onclick = function () { - window.parent.parent.focus(); - }; - } + handleNotification(msgRender.senderName, { + body: msgRender?.text || `[ ${msgRender.type} ]`, + ...(msgRender.type === 'photo' ? { image: msgRender.data.uri } : {}), }); } console.log('handleMessage*******************'); @@ -160,7 +151,7 @@ const conversationSlice = (set, get) => ({ return set((state) => ({ conversationsList: [...newConversations, ...state.conversationsList], activeConversations: { ...activeConversations, ...newConversationsMapped }, - totalNotify: state.totalNotify + newConversations.map(ele => ele.unread_msg_count).reduce((acc, cur) => acc + (cur || 0), 0), + totalNotify: state.totalNotify + newConversations.map((ele) => ele.unread_msg_count).reduce((acc, cur) => acc + (cur || 0), 0), })); }, delConversationitem: (conversation) => { @@ -180,10 +171,12 @@ const conversationSlice = (set, get) => ({ const { conversationsList, totalNotify } = get(); const targetId = conversation.sn; const targetIndex = conversationsList.findIndex((ele) => String(ele.sn) === String(targetId)); - targetIndex !== -1 ? conversationsList.splice(targetIndex, 1, { - ...conversationsList[targetIndex], - unread_msg_count: 0, - }) : null; + targetIndex !== -1 + ? conversationsList.splice(targetIndex, 1, { + ...conversationsList[targetIndex], + unread_msg_count: 0, + }) + : null; return set({ totalNotify: totalNotify - (conversation.unread_msg_count || 0), currentConversation: conversation, referenceMsg: {}, conversationsList: [...conversationsList] }); }, @@ -194,11 +187,13 @@ const messageSlice = (set, get) => ({ msgListLoading: false, activeConversations: {}, setMsgLoading: (msgListLoading) => set({ msgListLoading }), - receivedMessageList: (conversationid, msgList) => set((state) => ({ - msgListLoading: false, - activeConversations: { ...state.activeConversations, [String(conversationid)]: msgList } - })), - updateMessageItem: (message) => { // msgUpdate + receivedMessageList: (conversationid, msgList) => + set((state) => ({ + msgListLoading: false, + activeConversations: { ...state.activeConversations, [String(conversationid)]: msgList }, + })), + updateMessageItem: (message) => { + // msgUpdate console.log('UPDATE_SENT_MESSAGE_ITEM-----------------------------------------------------------------'); // 更新会话中的消息 const { activeConversations, conversationsList } = get(); @@ -233,7 +228,8 @@ const messageSlice = (set, get) => ({ // conversationsList: [...conversationsList], }); }, - sentOrReceivedNewMessage: (targetId, message) => { // msgRender: + sentOrReceivedNewMessage: (targetId, message) => { + // msgRender: const { activeConversations, conversationsList, currentConversation, totalNotify } = get(); const targetMsgs = activeConversations[String(targetId)] || []; const targetIndex = conversationsList.findIndex((ele) => String(ele.sn) === String(targetId)); @@ -257,7 +253,7 @@ const messageSlice = (set, get) => ({ conversationsList.splice(targetIndex, 1); conversationsList.unshift(newConversation); return set({ - totalNotify: totalNotify+newConversation.unread_msg_count, + totalNotify: totalNotify + newConversation.unread_msg_count, activeConversations: { ...activeConversations, [String(targetId)]: [...targetMsgs, message] }, conversationsList: [...conversationsList], currentConversation: { @@ -265,35 +261,36 @@ const messageSlice = (set, get) => ({ ...(String(targetId) === String(currentConversation.sn) ? { last_received_time: message.date } : {}), }, }); - }, }); -export const useConversationStore = create(devtools((set, get) => ({ - ...initialConversationState, - ...websocketSlice(set, get), - ...conversationSlice(set, get), - ...templatesSlice(set, get), - ...messageSlice(set, get), - ...referenceMsgSlice(set, get), - ...complexMsgSlice(set, get), - - // state actions - addError: (error) => set((state) => ({ errors: [...state.errors, error] })), - setInitial: (v) => set({ initialState: v }), - - // side effects - fetchInitialData: async (userId) => { - const { addToConversationList, setTemplates, setInitial } = get(); - - const conversationsList = await fetchConversationsList({ opisn: userId }); - addToConversationList(conversationsList); - - const templates = await fetchTemplates(); - setTemplates(templates); - - setInitial(true); - }, -}))); +export const useConversationStore = create( + devtools((set, get) => ({ + ...initialConversationState, + ...websocketSlice(set, get), + ...conversationSlice(set, get), + ...templatesSlice(set, get), + ...messageSlice(set, get), + ...referenceMsgSlice(set, get), + ...complexMsgSlice(set, get), + + // state actions + addError: (error) => set((state) => ({ errors: [...state.errors, error] })), + setInitial: (v) => set({ initialState: v }), + + // side effects + fetchInitialData: async (userId) => { + const { addToConversationList, setTemplates, setInitial } = get(); + + const conversationsList = await fetchConversationsList({ opisn: userId }); + addToConversationList(conversationsList); + + const templates = await fetchTemplates(); + setTemplates(templates); + + setInitial(true); + }, + })) +); export default useConversationStore; diff --git a/src/views/AuthApp.jsx b/src/views/AuthApp.jsx index e073693..370b371 100644 --- a/src/views/AuthApp.jsx +++ b/src/views/AuthApp.jsx @@ -43,6 +43,12 @@ function AuthApp() { } }, []) + useEffect(() => { + Notification.requestPermission(); + return () => {}; + }, []) + + let defaultPath = 'follow' if (href !== '/') { @@ -54,7 +60,9 @@ function AuthApp() { token: { colorBgContainer }, } = theme.useToken() - // Flicker title when new message received + /** + * 标签页标题闪烁 + */ const [isTitleVisible, setIsTitleVisible] = useState(true); useEffect(() => { let interval; @@ -62,36 +70,13 @@ function AuthApp() { interval = setInterval(() => { document.title = isTitleVisible ? `✉🔔🔥【${totalNotify}条新消息】` : '聊天式销售平台'; setIsTitleVisible(!isTitleVisible); - }, 600); + }, 500); } else { document.title = '聊天式销售平台'; } return () => clearInterval(interval); }, [totalNotify, isTitleVisible]); - // Display browser notification and change favicon when new message received - // useEffect(() => { - // if (hasNewMessage) { - // if (Notification.permission === "granted") { - // new Notification("New messages received!", { - // body: "You have new messages.", - // icon: "path-to-your-icon.png", - // }); - // } else if (Notification.permission !== "denied") { - // Notification.requestPermission().then(permission => { - // if (permission === "granted") { - // new Notification("New messages received!", { - // body: "You have new messages.", - // icon: "path-to-your-icon.png", - // }); - // } - // }); - // } - - // setFavicon('path-to-your-new-favicon.ico'); - // } - // }, [hasNewMessage]); - return (