新消息闪烁提醒

dev/chat
Lei OT 1 year ago
parent f9abf66455
commit fd204762d7

@ -45,7 +45,7 @@ const websocketSlice = (set, get) => ({
setWebsocketRetrytimes: (retrytimes) => set({ websocketRetrytimes: retrytimes, websocketRetrying: retrytimes > 0 }), setWebsocketRetrytimes: (retrytimes) => set({ websocketRetrytimes: retrytimes, websocketRetrying: retrytimes > 0 }),
connectWebsocket: (userId) => { connectWebsocket: (userId) => {
const { setWebsocket, setWebsocketOpened, setWebsocketRetrytimes, addError, handleMessage } = get(); const { setWebsocket, setWebsocketOpened, setWebsocketRetrytimes, addError, handleMessage, activeConversations } = get();
const realtimeAPI = new RealTimeAPI( const realtimeAPI = new RealTimeAPI(
{ {
@ -56,7 +56,15 @@ const websocketSlice = (set, get) => ({
setWebsocketOpened(true); setWebsocketOpened(true);
setWebsocketRetrytimes(0); setWebsocketRetrytimes(0);
}, },
() => setWebsocketOpened(false), () => {
setWebsocketOpened(false)
const newMsgList = Object.keys(activeConversations).reduce((acc, key) => {
const newMsgList = activeConversations[key].slice(-10);
acc[key] = newMsgList;
return acc;
}, {});
set({ activeConversations: newMsgList });
},
(n) => setWebsocketRetrytimes(n) (n) => setWebsocketRetrytimes(n)
); );
@ -108,6 +116,8 @@ const websocketSlice = (set, get) => ({
if (permission === 'granted') { if (permission === 'granted') {
const notification = new Notification(`${msgRender.senderName}`, { const notification = new Notification(`${msgRender.senderName}`, {
body: msgRender?.text || `[ ${msgRender.type} ]`, body: msgRender?.text || `[ ${msgRender.type} ]`,
requireInteraction: true, // 设置手动关闭
tag: 'global-sales-notification', // 通知ID同类通知建议设置相同ID避免通知过多遮挡桌面
...(msgRender.type === 'photo' ? { image: msgRender.data.uri } : {}), ...(msgRender.type === 'photo' ? { image: msgRender.data.uri } : {}),
}); });
notification.onclick = function () { notification.onclick = function () {
@ -149,7 +159,8 @@ 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),
})); }));
}, },
delConversationitem: (conversation) => { delConversationitem: (conversation) => {
@ -158,15 +169,15 @@ const conversationSlice = (set, get) => ({
const targetIndex = conversationsList.findIndex((ele) => String(ele.sn) === String(targetId)); const targetIndex = conversationsList.findIndex((ele) => String(ele.sn) === String(targetId));
conversationsList.splice(targetIndex, 1); conversationsList.splice(targetIndex, 1);
return set((state) => ({ return set({
conversationsList: [...conversationsList], conversationsList: [...conversationsList],
activeConversations: { ...activeConversations, [`${targetId}`]: [] }, activeConversations: { ...activeConversations, [`${targetId}`]: [] },
currentConversation: {}, currentConversation: {},
})); });
}, },
setCurrentConversation: (conversation) => { setCurrentConversation: (conversation) => {
// 清空未读 // 清空未读
const { conversationsList } = 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.splice(targetIndex, 1, {
@ -174,11 +185,12 @@ const conversationSlice = (set, get) => ({
unread_msg_count: 0, unread_msg_count: 0,
}) : null; }) : null;
return set({ currentConversation: conversation, referenceMsg: {}, conversationsList: [...conversationsList] }); return set({ totalNotify: totalNotify - conversation.unread_msg_count, currentConversation: conversation, referenceMsg: {}, conversationsList: [...conversationsList] });
}, },
}); });
const messageSlice = (set, get) => ({ const messageSlice = (set, get) => ({
totalNotify: 0,
msgListLoading: false, msgListLoading: false,
activeConversations: {}, activeConversations: {},
setMsgLoading: (msgListLoading) => set({ msgListLoading }), setMsgLoading: (msgListLoading) => set({ msgListLoading }),
@ -216,13 +228,13 @@ const messageSlice = (set, get) => ({
// }); // });
// } // }
return set((state) => ({ return set({
activeConversations: { ...activeConversations, [String(targetId)]: targetMsgs }, activeConversations: { ...activeConversations, [String(targetId)]: targetMsgs },
conversationsList: [...conversationsList], // conversationsList: [...conversationsList],
})); });
}, },
sentOrReceivedNewMessage: (targetId, message) => { // msgRender: sentOrReceivedNewMessage: (targetId, message) => { // msgRender:
const { activeConversations, conversationsList, currentConversation } = 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));
const lastReceivedTime = message.type !== 'system' && message.sender !== 'me' ? message.date : null; const lastReceivedTime = message.type !== 'system' && message.sender !== 'me' ? message.date : null;
@ -244,14 +256,15 @@ const messageSlice = (set, get) => ({
}; };
conversationsList.splice(targetIndex, 1); conversationsList.splice(targetIndex, 1);
conversationsList.unshift(newConversation); conversationsList.unshift(newConversation);
return set((state) => ({ return set({
totalNotify: totalNotify+newConversation.unread_msg_count,
activeConversations: { ...activeConversations, [String(targetId)]: [...targetMsgs, message] }, activeConversations: { ...activeConversations, [String(targetId)]: [...targetMsgs, message] },
conversationsList: [...conversationsList], conversationsList: [...conversationsList],
currentConversation: { currentConversation: {
...currentConversation, ...currentConversation,
...(String(targetId) === String(currentConversation.sn) ? { last_received_time: message.date } : {}), ...(String(targetId) === String(currentConversation.sn) ? { last_received_time: message.date } : {}),
}, },
})); });
}, },
}); });

@ -3,10 +3,10 @@ import useAuthStore from '@/stores/AuthStore'
import useConversationStore from '@/stores/ConversationStore' import useConversationStore from '@/stores/ConversationStore'
import { useThemeContext } from '@/stores/ThemeContext' import { useThemeContext } from '@/stores/ThemeContext'
import { DownOutlined } from '@ant-design/icons' import { DownOutlined } from '@ant-design/icons'
import { App as AntApp, Avatar, Col, ConfigProvider, Dropdown, Empty, Layout, Menu, Row, Space, Typography, theme } from 'antd' import { App as AntApp, Avatar, Col, ConfigProvider, Dropdown, Empty, Layout, Menu, Row, Space, Typography, theme, Badge } from 'antd'
import zhLocale from 'antd/locale/zh_CN' import zhLocale from 'antd/locale/zh_CN'
import 'dayjs/locale/zh-cn' import 'dayjs/locale/zh-cn'
import { useEffect } from 'react' import { useEffect, useState } from 'react'
import { Link, NavLink, Outlet, useHref, useNavigate } from 'react-router-dom' import { Link, NavLink, Outlet, useHref, useNavigate } from 'react-router-dom'
import '@/assets/App.css' import '@/assets/App.css'
@ -22,7 +22,7 @@ function AuthApp() {
const { colorPrimary, borderRadius } = useThemeContext() const { colorPrimary, borderRadius } = useThemeContext()
const { loginUser } = useAuthStore() const { loginUser } = useAuthStore()
const href = useHref() const href = useHref()
useEffect(() => { useEffect(() => {
@ -32,7 +32,7 @@ function AuthApp() {
} }
}, [href]) }, [href])
const totalNotify = useConversationStore((state) => state.totalNotify);
useEffect(() => { useEffect(() => {
if (loginUser.userId > 0) { if (loginUser.userId > 0) {
useConversationStore.getState().connectWebsocket(loginUser.userId); useConversationStore.getState().connectWebsocket(loginUser.userId);
@ -54,6 +54,44 @@ function AuthApp() {
token: { colorBgContainer }, token: { colorBgContainer },
} = theme.useToken() } = theme.useToken()
// Flicker title when new message received
const [isTitleVisible, setIsTitleVisible] = useState(true);
useEffect(() => {
let interval;
if (totalNotify > 0) {
interval = setInterval(() => {
document.title = isTitleVisible ? `✉🔔🔥【${totalNotify}条新消息】` : '聊天式销售平台';
setIsTitleVisible(!isTitleVisible);
}, 600);
} 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 ( return (
<ConfigProvider <ConfigProvider
theme={{ theme={{
@ -85,7 +123,13 @@ function AuthApp() {
selectedKeys={[defaultPath]} selectedKeys={[defaultPath]}
items={[ items={[
{ key: '/order/follow', label: <Link to='/order/follow'>订单跟踪</Link> }, { key: '/order/follow', label: <Link to='/order/follow'>订单跟踪</Link> },
{ key: '/order/chat', label: <Link to='/order/chat'>在线聊天</Link> }, { key: '/order/chat', label: <Link to='/order/chat'>在线聊天
<Badge
count={totalNotify}
style={{
backgroundColor: '#52c41a',
}}
/></Link> },
{ key: '/chat/history', label: <Link to='/chat/history'>聊天历史</Link> }, { key: '/chat/history', label: <Link to='/chat/history'>聊天历史</Link> },
]} ]}
/> />

Loading…
Cancel
Save