bigbluebutton-Github/bigbluebutton-html5/imports/ui/components/chat/alert/component.jsx

270 lines
8.9 KiB
React
Raw Normal View History

2021-05-28 01:46:27 +08:00
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
2021-05-28 02:12:49 +08:00
import { Meteor } from 'meteor/meteor';
import { defineMessages, injectIntl } from 'react-intl';
2022-02-16 01:57:50 +08:00
import injectNotify from '/imports/ui/components/common/toast/inject-notify/component';
2021-05-28 01:46:27 +08:00
import AudioService from '/imports/ui/components/audio/service';
import ChatPushAlert from './push-alert/component';
2023-02-23 22:23:51 +08:00
import { stripTags, unescapeHtml, uniqueId } from '/imports/utils/string-utils';
import Service from '../service';
2021-11-02 19:32:07 +08:00
import Styled from './styles';
2022-05-31 02:49:19 +08:00
import { usePreviousValue } from '/imports/ui/components/utils/hooks';
import { Session } from 'meteor/session';
2023-02-23 21:27:16 +08:00
import { isEqual } from 'radash';
2021-05-18 04:25:07 +08:00
const CHAT_CONFIG = Meteor.settings.public.chat;
const PUBLIC_CHAT_CLEAR = CHAT_CONFIG.chat_clear;
2021-06-07 22:18:57 +08:00
const PUBLIC_CHAT_ID = CHAT_CONFIG.public_id;
const POLL_RESULT_KEY = CHAT_CONFIG.system_messages_keys.chat_poll_result;
const EXPORTED_PRESENTATION_KEY = CHAT_CONFIG.system_messages_keys.chat_exported_presentation;
2021-05-18 04:25:07 +08:00
const propTypes = {
2021-05-28 01:46:27 +08:00
pushAlertEnabled: PropTypes.bool.isRequired,
audioAlertEnabled: PropTypes.bool.isRequired,
2021-05-28 03:11:36 +08:00
unreadMessagesCountByChat: PropTypes.arrayOf(PropTypes.object),
unreadMessagesByChat: PropTypes.arrayOf(PropTypes.array),
idChatOpen: PropTypes.string.isRequired,
2021-04-10 04:35:05 +08:00
intl: PropTypes.shape({
formatMessage: PropTypes.func.isRequired,
}).isRequired,
};
2021-05-28 01:46:27 +08:00
const defaultProps = {
unreadMessagesCountByChat: null,
unreadMessagesByChat: null,
};
const intlMessages = defineMessages({
appToastChatPublic: {
id: 'app.toast.chat.public',
description: 'when entry various message',
},
appToastChatPrivate: {
id: 'app.toast.chat.private',
description: 'when entry various message',
},
appToastChatSystem: {
id: 'app.toast.chat.system',
description: 'system for use',
},
publicChatClear: {
id: 'app.chat.clearPublicChatMessage',
description: 'message of when clear the public chat',
},
2022-01-20 08:43:42 +08:00
publicChatMsg: {
id: 'app.toast.chat.public',
description: 'public chat toast message title',
},
privateChatMsg: {
id: 'app.toast.chat.private',
description: 'private chat toast message title',
},
pollResults: {
id: 'app.toast.chat.poll',
description: 'chat toast message for polls',
},
pollResultsClick: {
id: 'app.toast.chat.pollClick',
description: 'chat toast click message for polls',
},
exportedPresentation: {
id: 'app.toast.chat.exportedPresentation',
description: 'chat toast message for exportedPresentation',
}
});
2019-01-14 21:23:35 +08:00
const ALERT_INTERVAL = 5000; // 5 seconds
const ALERT_DURATION = 4000; // 4 seconds
2021-05-28 01:46:27 +08:00
const ChatAlert = (props) => {
const {
audioAlertEnabled,
pushAlertEnabled,
idChatOpen,
unreadMessagesCountByChat,
unreadMessagesByChat,
intl,
2021-08-05 19:03:24 +08:00
layoutContextDispatch,
2021-05-28 01:46:27 +08:00
} = props;
const [unreadMessagesCount, setUnreadMessagesCount] = useState(0);
const [unreadMessages, setUnreadMessages] = useState([]);
const [lastAlertTimestampByChat, setLastAlertTimestampByChat] = useState({});
const [alertEnabledTimestamp, setAlertEnabledTimestamp] = useState(null);
2022-05-31 02:49:19 +08:00
const prevUnreadMessages = usePreviousValue(unreadMessages);
2021-05-28 01:46:27 +08:00
// audio alerts
useEffect(() => {
if (audioAlertEnabled) {
2021-05-28 03:11:36 +08:00
const unreadObject = unreadMessagesCountByChat;
2021-05-28 01:46:27 +08:00
const unreadCount = document.hidden
2021-05-28 02:12:49 +08:00
? unreadObject.reduce((a, b) => a + b.unreadCounter, 0)
: unreadObject.filter((chat) => chat.chatId !== idChatOpen)
.reduce((a, b) => a + b.unreadCounter, 0);
2021-05-28 01:46:27 +08:00
2021-08-27 21:47:58 +08:00
if (unreadCount > unreadMessagesCount) {
2021-05-28 01:46:27 +08:00
AudioService.playAlertSound(`${Meteor.settings.public.app.cdn
+ Meteor.settings.public.app.basename
+ Meteor.settings.public.app.instanceId}`
+ '/resources/sounds/notify.mp3');
}
setUnreadMessagesCount(unreadCount);
}
2021-05-28 01:46:27 +08:00
}, [unreadMessagesCountByChat]);
2021-05-28 01:46:27 +08:00
// push alerts
useEffect(() => {
if (pushAlertEnabled) {
setAlertEnabledTimestamp(new Date().getTime());
}
2021-05-28 01:46:27 +08:00
}, [pushAlertEnabled]);
useEffect(() => {
if (pushAlertEnabled) {
2021-05-28 03:11:36 +08:00
const alertsObject = unreadMessagesByChat;
2021-05-28 01:46:27 +08:00
let timewindowsToAlert = [];
let filteredTimewindows = [];
2021-05-28 02:12:49 +08:00
alertsObject.forEach((chat) => {
2021-05-28 01:46:27 +08:00
filteredTimewindows = filteredTimewindows.concat(
2021-05-28 02:12:49 +08:00
chat.filter((timeWindow) => timeWindow.timestamp > alertEnabledTimestamp),
2021-05-28 01:46:27 +08:00
);
2021-05-28 02:12:49 +08:00
});
2021-05-28 01:46:27 +08:00
2021-05-28 02:12:49 +08:00
filteredTimewindows.forEach((timeWindow) => {
2021-05-28 01:46:27 +08:00
const durationDiff = ALERT_DURATION - (new Date().getTime() - timeWindow.timestamp);
2021-05-28 03:11:36 +08:00
if ((timeWindow.lastTimestamp > timeWindow.timestamp && durationDiff > 0
&& timeWindow.lastTimestamp > (lastAlertTimestampByChat[timeWindow.chatId] || 0))
|| timeWindow.timestamp
2021-05-28 02:12:49 +08:00
> (lastAlertTimestampByChat[timeWindow.chatId] || 0) + ALERT_INTERVAL) {
timewindowsToAlert = timewindowsToAlert
.filter((item) => item.chatId !== timeWindow.chatId);
2021-05-28 03:11:36 +08:00
const newTimeWindow = { ...timeWindow };
newTimeWindow.durationDiff = durationDiff;
timewindowsToAlert.push(newTimeWindow);
2021-05-28 02:12:49 +08:00
2021-08-05 21:14:59 +08:00
const newLastAlertTimestampByChat = { ...lastAlertTimestampByChat };
2021-05-28 02:12:49 +08:00
if (timeWindow.timestamp > (lastAlertTimestampByChat[timeWindow.chatId] || 0)) {
2021-08-05 21:14:59 +08:00
newLastAlertTimestampByChat[timeWindow.chatId] = timeWindow.timestamp;
setLastAlertTimestampByChat(newLastAlertTimestampByChat);
2021-05-28 01:46:27 +08:00
}
}
2021-05-28 02:12:49 +08:00
});
2021-05-28 01:46:27 +08:00
setUnreadMessages(timewindowsToAlert);
2019-01-14 21:23:35 +08:00
}
2021-05-28 01:46:27 +08:00
}, [unreadMessagesByChat]);
2021-05-28 01:46:27 +08:00
const mapContentText = (message) => {
const contentMessage = message
.map((content) => {
2021-06-07 22:18:57 +08:00
if (content.text === PUBLIC_CHAT_CLEAR) {
return intl.formatMessage(intlMessages.publicChatClear);
}
return unescapeHtml(stripTags(content.text));
});
2019-01-14 21:23:35 +08:00
return contentMessage;
2021-05-28 02:12:49 +08:00
};
const createMessage = (name, message) => (
2021-11-02 19:32:07 +08:00
<Styled.PushMessageContent>
<Styled.UserNameMessage>{name}</Styled.UserNameMessage>
<Styled.ContentMessage>
2021-05-28 02:12:49 +08:00
{
mapContentText(message)
2023-02-23 22:23:51 +08:00
.reduce((acc, text) => [...acc, (<br key={uniqueId('br_')} />), text], [])
2021-05-28 02:12:49 +08:00
}
2021-11-02 19:32:07 +08:00
</Styled.ContentMessage>
</Styled.PushMessageContent>
2021-05-28 02:12:49 +08:00
);
const createPollMessage = () => (
<Styled.PushMessageContent>
<Styled.UserNameMessage>
{intl.formatMessage(intlMessages.pollResults)}
</Styled.UserNameMessage>
<Styled.ContentMessagePoll>
{intl.formatMessage(intlMessages.pollResultsClick)}
</Styled.ContentMessagePoll>
</Styled.PushMessageContent>
);
const createExportedPresentationMessage = (filename) => (
<Styled.PushMessageContent>
<Styled.UserNameMessage>{intl.formatMessage(intlMessages.exportedPresentation)}</Styled.UserNameMessage>
<Styled.ContentMessageExportedPresentation>{filename}</Styled.ContentMessageExportedPresentation>
</Styled.PushMessageContent>
);
2023-02-23 21:27:16 +08:00
if (isEqual(prevUnreadMessages, unreadMessages)) {
return null;
}
2021-05-28 01:46:27 +08:00
return pushAlertEnabled
2021-05-28 02:12:49 +08:00
? unreadMessages.map((timeWindow) => {
2021-05-28 01:46:27 +08:00
const mappedMessage = Service.mapGroupMessage(timeWindow);
let content = null;
let isPollResult = false;
if (mappedMessage) {
if (mappedMessage.id.includes(POLL_RESULT_KEY)) {
content = createPollMessage();
isPollResult = true;
} else if (mappedMessage.id.includes(EXPORTED_PRESENTATION_KEY)) {
content = createExportedPresentationMessage(mappedMessage.extra.filename);
} else {
content = createMessage(mappedMessage.sender.name, mappedMessage.content.slice(-5));
}
}
2021-05-28 02:12:49 +08:00
2021-06-07 22:18:57 +08:00
const messageChatId = mappedMessage.chatId === 'MAIN-PUBLIC-GROUP-CHAT' ? PUBLIC_CHAT_ID : mappedMessage.chatId;
2021-06-08 00:53:35 +08:00
const newUnreadMessages = unreadMessages
.filter((message) => message.key !== mappedMessage.key);
2021-05-28 02:12:49 +08:00
return content
? (
<ChatPushAlert
2021-06-07 22:18:57 +08:00
key={messageChatId}
chatId={messageChatId}
2021-05-28 02:12:49 +08:00
content={content}
title={
(mappedMessage.chatId === 'MAIN-PUBLIC-GROUP-CHAT')
? <span>{intl.formatMessage(intlMessages.appToastChatPublic)}</span>
: <span>{intl.formatMessage(intlMessages.appToastChatPrivate)}</span>
}
onOpen={
() => {
if (isPollResult) {
Session.set('ignorePollNotifications', true);
}
setUnreadMessages(newUnreadMessages);
}
2021-06-08 00:53:35 +08:00
}
onClose={
() => {
if (isPollResult) {
Session.set('ignorePollNotifications', false);
}
setUnreadMessages(newUnreadMessages);
}
2021-05-28 02:12:49 +08:00
}
2021-05-28 03:11:36 +08:00
alertDuration={timeWindow.durationDiff}
2021-08-05 19:03:24 +08:00
layoutContextDispatch={layoutContextDispatch}
2021-05-28 02:12:49 +08:00
/>
) : null;
2021-05-28 01:46:27 +08:00
})
: null;
};
ChatAlert.propTypes = propTypes;
2021-05-28 01:46:27 +08:00
ChatAlert.defaultProps = defaultProps;
2022-01-20 08:43:42 +08:00
export default injectNotify(injectIntl(ChatAlert));