新消息提醒和闪烁

dev/chat
Lei OT 2 years ago
parent 323247aa0f
commit 32076e9441

@ -370,3 +370,35 @@ export const whatsappError = {
'131048': '[131048] 账户被风控.', // 消息发送太多, 达到垃圾数量限制 '131048': '[131048] 账户被风控.', // 消息发送太多, 达到垃圾数量限制
'131031': '[131031] 账户已锁定.', '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(); // 关闭通知,适用于设置了手动关闭的通知
};
};

@ -1,7 +1,7 @@
import { create } from 'zustand'; import { create } from 'zustand';
import { RealTimeAPI } from '@/lib/realTimeAPI'; import { RealTimeAPI } from '@/lib/realTimeAPI';
import { olog, isEmpty } from '@/utils/utils'; import { olog, isEmpty } from '@/utils/utils';
import { receivedMsgTypeMapped } from '@/lib/msgUtils'; import { receivedMsgTypeMapped, handleNotification } from '@/lib/msgUtils';
import { fetchConversationsList, fetchTemplates } from '@/actions/ConversationActions'; import { fetchConversationsList, fetchTemplates } from '@/actions/ConversationActions';
import { devtools } from 'zustand/middleware'; import { devtools } from 'zustand/middleware';
import { WS_URL } from '@/config'; import { WS_URL } from '@/config';
@ -57,7 +57,7 @@ const websocketSlice = (set, get) => ({
setWebsocketRetrytimes(0); setWebsocketRetrytimes(0);
}, },
() => { () => {
setWebsocketOpened(false) setWebsocketOpened(false);
const newMsgList = Object.keys(activeConversations).reduce((acc, key) => { const newMsgList = Object.keys(activeConversations).reduce((acc, key) => {
const newMsgList = activeConversations[key].slice(-10); const newMsgList = activeConversations[key].slice(-10);
acc[key] = newMsgList; acc[key] = newMsgList;
@ -72,7 +72,7 @@ const websocketSlice = (set, get) => ({
realtimeAPI.onMessage(handleMessage); realtimeAPI.onMessage(handleMessage);
realtimeAPI.onCompletion(() => addError('Connection broken')); realtimeAPI.onCompletion(() => addError('Connection broken'));
olog('Connecting to websocket...', realtimeAPI) olog('Connecting to websocket...', realtimeAPI);
setWebsocket(realtimeAPI); setWebsocket(realtimeAPI);
}, },
disconnectWebsocket: () => { disconnectWebsocket: () => {
@ -81,7 +81,7 @@ const websocketSlice = (set, get) => ({
return set({ websocket: null }); return set({ websocket: null });
}, },
reconnectWebsocket: (userId) => { reconnectWebsocket: (userId) => {
const {disconnectWebsocket, connectWebsocket} = get(); const { disconnectWebsocket, connectWebsocket } = get();
disconnectWebsocket(); disconnectWebsocket();
setTimeout(() => { setTimeout(() => {
connectWebsocket(userId); connectWebsocket(userId);
@ -112,18 +112,9 @@ const websocketSlice = (set, get) => ({
} }
if (!isEmpty(msgRender)) { if (!isEmpty(msgRender)) {
sentOrReceivedNewMessage(msgRender.conversationid, msgRender); sentOrReceivedNewMessage(msgRender.conversationid, msgRender);
window.Notification.requestPermission().then(function (permission) { handleNotification(msgRender.senderName, {
if (permission === 'granted') { body: msgRender?.text || `[ ${msgRender.type} ]`,
const notification = new Notification(`${msgRender.senderName}`, { ...(msgRender.type === 'photo' ? { image: msgRender.data.uri } : {}),
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();
};
}
}); });
} }
console.log('handleMessage*******************'); console.log('handleMessage*******************');
@ -160,7 +151,7 @@ const conversationSlice = (set, get) => ({
return set((state) => ({ return set((state) => ({
conversationsList: [...newConversations, ...state.conversationsList], conversationsList: [...newConversations, ...state.conversationsList],
activeConversations: { ...activeConversations, ...newConversationsMapped }, 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) => { delConversationitem: (conversation) => {
@ -180,10 +171,12 @@ const conversationSlice = (set, get) => ({
const { conversationsList, totalNotify } = get(); const { conversationsList, totalNotify } = get();
const targetId = conversation.sn; const targetId = conversation.sn;
const targetIndex = conversationsList.findIndex((ele) => String(ele.sn) === String(targetId)); const targetIndex = conversationsList.findIndex((ele) => String(ele.sn) === String(targetId));
targetIndex !== -1 ? conversationsList.splice(targetIndex, 1, { targetIndex !== -1
...conversationsList[targetIndex], ? conversationsList.splice(targetIndex, 1, {
unread_msg_count: 0, ...conversationsList[targetIndex],
}) : null; unread_msg_count: 0,
})
: null;
return set({ totalNotify: totalNotify - (conversation.unread_msg_count || 0), currentConversation: conversation, referenceMsg: {}, conversationsList: [...conversationsList] }); return set({ totalNotify: totalNotify - (conversation.unread_msg_count || 0), currentConversation: conversation, referenceMsg: {}, conversationsList: [...conversationsList] });
}, },
@ -194,11 +187,13 @@ const messageSlice = (set, get) => ({
msgListLoading: false, msgListLoading: false,
activeConversations: {}, activeConversations: {},
setMsgLoading: (msgListLoading) => set({ msgListLoading }), setMsgLoading: (msgListLoading) => set({ msgListLoading }),
receivedMessageList: (conversationid, msgList) => set((state) => ({ receivedMessageList: (conversationid, msgList) =>
msgListLoading: false, set((state) => ({
activeConversations: { ...state.activeConversations, [String(conversationid)]: msgList } msgListLoading: false,
})), activeConversations: { ...state.activeConversations, [String(conversationid)]: msgList },
updateMessageItem: (message) => { // msgUpdate })),
updateMessageItem: (message) => {
// msgUpdate
console.log('UPDATE_SENT_MESSAGE_ITEM-----------------------------------------------------------------'); console.log('UPDATE_SENT_MESSAGE_ITEM-----------------------------------------------------------------');
// 更新会话中的消息 // 更新会话中的消息
const { activeConversations, conversationsList } = get(); const { activeConversations, conversationsList } = get();
@ -233,7 +228,8 @@ const messageSlice = (set, get) => ({
// conversationsList: [...conversationsList], // conversationsList: [...conversationsList],
}); });
}, },
sentOrReceivedNewMessage: (targetId, message) => { // msgRender: sentOrReceivedNewMessage: (targetId, message) => {
// msgRender:
const { activeConversations, conversationsList, currentConversation, totalNotify } = get(); const { activeConversations, conversationsList, currentConversation, totalNotify } = get();
const targetMsgs = activeConversations[String(targetId)] || []; const targetMsgs = activeConversations[String(targetId)] || [];
const targetIndex = conversationsList.findIndex((ele) => String(ele.sn) === 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.splice(targetIndex, 1);
conversationsList.unshift(newConversation); conversationsList.unshift(newConversation);
return set({ return set({
totalNotify: totalNotify+newConversation.unread_msg_count, totalNotify: totalNotify + newConversation.unread_msg_count,
activeConversations: { ...activeConversations, [String(targetId)]: [...targetMsgs, message] }, activeConversations: { ...activeConversations, [String(targetId)]: [...targetMsgs, message] },
conversationsList: [...conversationsList], conversationsList: [...conversationsList],
currentConversation: { currentConversation: {
@ -265,35 +261,36 @@ const messageSlice = (set, get) => ({
...(String(targetId) === String(currentConversation.sn) ? { last_received_time: message.date } : {}), ...(String(targetId) === String(currentConversation.sn) ? { last_received_time: message.date } : {}),
}, },
}); });
}, },
}); });
export const useConversationStore = create(devtools((set, get) => ({ export const useConversationStore = create(
...initialConversationState, devtools((set, get) => ({
...websocketSlice(set, get), ...initialConversationState,
...conversationSlice(set, get), ...websocketSlice(set, get),
...templatesSlice(set, get), ...conversationSlice(set, get),
...messageSlice(set, get), ...templatesSlice(set, get),
...referenceMsgSlice(set, get), ...messageSlice(set, get),
...complexMsgSlice(set, get), ...referenceMsgSlice(set, get),
...complexMsgSlice(set, get),
// state actions
addError: (error) => set((state) => ({ errors: [...state.errors, error] })), // state actions
setInitial: (v) => set({ initialState: v }), addError: (error) => set((state) => ({ errors: [...state.errors, error] })),
setInitial: (v) => set({ initialState: v }),
// side effects
fetchInitialData: async (userId) => { // side effects
const { addToConversationList, setTemplates, setInitial } = get(); fetchInitialData: async (userId) => {
const { addToConversationList, setTemplates, setInitial } = get();
const conversationsList = await fetchConversationsList({ opisn: userId });
addToConversationList(conversationsList); const conversationsList = await fetchConversationsList({ opisn: userId });
addToConversationList(conversationsList);
const templates = await fetchTemplates();
setTemplates(templates); const templates = await fetchTemplates();
setTemplates(templates);
setInitial(true);
}, setInitial(true);
}))); },
}))
);
export default useConversationStore; export default useConversationStore;

@ -43,6 +43,12 @@ function AuthApp() {
} }
}, []) }, [])
useEffect(() => {
Notification.requestPermission();
return () => {};
}, [])
let defaultPath = 'follow' let defaultPath = 'follow'
if (href !== '/') { if (href !== '/') {
@ -54,7 +60,9 @@ function AuthApp() {
token: { colorBgContainer }, token: { colorBgContainer },
} = theme.useToken() } = theme.useToken()
// Flicker title when new message received /**
* 标签页标题闪烁
*/
const [isTitleVisible, setIsTitleVisible] = useState(true); const [isTitleVisible, setIsTitleVisible] = useState(true);
useEffect(() => { useEffect(() => {
let interval; let interval;
@ -62,36 +70,13 @@ function AuthApp() {
interval = setInterval(() => { interval = setInterval(() => {
document.title = isTitleVisible ? `✉🔔🔥【${totalNotify}条新消息】` : '聊天式销售平台'; document.title = isTitleVisible ? `✉🔔🔥【${totalNotify}条新消息】` : '聊天式销售平台';
setIsTitleVisible(!isTitleVisible); setIsTitleVisible(!isTitleVisible);
}, 600); }, 500);
} else { } else {
document.title = '聊天式销售平台'; document.title = '聊天式销售平台';
} }
return () => clearInterval(interval); return () => clearInterval(interval);
}, [totalNotify, isTitleVisible]); }, [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 ( return (
<ConfigProvider <ConfigProvider
theme={{ theme={{

Loading…
Cancel
Save