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

251 lines
8.1 KiB
React
Raw Normal View History

import React, { Fragment, PureComponent } from 'react';
import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl';
import _ from 'lodash';
import UnreadMessages from '/imports/ui/services/unread-messages';
import ChatAudioAlert from './audio-alert/component';
import ChatPushAlert from './push-alert/component';
import Service from '../service';
import { styles } from '../styles';
const propTypes = {
2019-01-14 21:23:35 +08:00
pushAlertDisabled: PropTypes.bool.isRequired,
activeChats: PropTypes.arrayOf(PropTypes.object).isRequired,
audioAlertDisabled: PropTypes.bool.isRequired,
joinTimestamp: PropTypes.number.isRequired,
idChatOpen: PropTypes.string.isRequired,
2021-04-10 04:35:05 +08:00
publicChatId: PropTypes.string.isRequired,
intl: PropTypes.shape({
formatMessage: PropTypes.func.isRequired,
}).isRequired,
};
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',
},
});
2019-01-14 21:23:35 +08:00
const ALERT_INTERVAL = 5000; // 5 seconds
const ALERT_DURATION = 4000; // 4 seconds
class ChatAlert extends PureComponent {
constructor(props) {
super(props);
const { joinTimestamp } = props;
this.state = {
alertEnabledTimestamp: joinTimestamp,
2019-01-14 21:23:35 +08:00
lastAlertTimestampByChat: {},
pendingNotificationsByChat: {},
};
}
2019-01-14 21:23:35 +08:00
componentDidUpdate(prevProps) {
const {
2019-01-14 21:23:35 +08:00
activeChats,
idChatOpen,
2019-01-14 21:23:35 +08:00
joinTimestamp,
pushAlertDisabled,
2021-04-10 04:35:05 +08:00
messages,
publicChatId,
} = this.props;
2019-01-14 21:23:35 +08:00
const {
2019-01-15 00:23:11 +08:00
alertEnabledTimestamp,
2019-01-14 21:23:35 +08:00
lastAlertTimestampByChat,
pendingNotificationsByChat,
} = this.state;
2019-01-14 21:23:35 +08:00
// Avoid alerting messages received before enabling alerts
if (prevProps.pushAlertDisabled && !pushAlertDisabled) {
const newAlertEnabledTimestamp = Service.getLastMessageTimestampFromChatList(activeChats, messages);
2019-01-16 21:08:20 +08:00
this.setAlertEnabledTimestamp(newAlertEnabledTimestamp);
return;
}
2019-01-14 21:23:35 +08:00
// Keep track of messages that was not alerted yet
const unalertedMessagesByChatId = {};
activeChats
2021-05-08 04:33:22 +08:00
.filter(chat => chat.chatId !== idChatOpen)
2019-01-14 21:23:35 +08:00
.filter(chat => chat.unreadCounter > 0)
.forEach((chat) => {
2021-05-08 04:33:22 +08:00
const chatId = (chat.chatId === 'public') ? publicChatId : chat.chatId;
2021-04-10 04:35:05 +08:00
const thisChatUnreadMessages = UnreadMessages.getUnreadMessages(chatId, messages);
2019-01-14 21:23:35 +08:00
unalertedMessagesByChatId[chatId] = thisChatUnreadMessages.filter((msg) => {
const retorno = (msg
2019-01-15 00:23:11 +08:00
&& msg.timestamp > alertEnabledTimestamp
2019-01-14 21:23:35 +08:00
&& msg.timestamp > joinTimestamp
2021-04-10 04:35:05 +08:00
&& msg.timestamp > (lastAlertTimestampByChat[chatId] || 0)
&& !pushAlertDisabled
2019-01-14 21:23:35 +08:00
);
return retorno;
});
if (!unalertedMessagesByChatId[chatId].length) delete unalertedMessagesByChatId[chatId];
});
2019-01-14 21:23:35 +08:00
const lastUnalertedMessageTimestampByChat = {};
Object.keys(unalertedMessagesByChatId).forEach((chatId) => {
lastUnalertedMessageTimestampByChat[chatId] = unalertedMessagesByChatId[chatId]
.reduce(Service.maxTimestampReducer, 0);
});
2019-01-14 21:23:35 +08:00
// Keep track of chats that need to be alerted now (considering alert interval)
const chatsWithPendingAlerts = Object.keys(lastUnalertedMessageTimestampByChat)
.filter(chatId => lastUnalertedMessageTimestampByChat[chatId]
> ((lastAlertTimestampByChat[chatId] || 0) + ALERT_INTERVAL)
&& !(chatId in pendingNotificationsByChat));
if (idChatOpen !== prevProps.idChatOpen) {
this.setChatMessagesState({}, { ...lastAlertTimestampByChat });
}
2019-01-14 21:23:35 +08:00
if (!chatsWithPendingAlerts.length) return;
const newPendingNotificationsByChat = Object.assign({},
...chatsWithPendingAlerts.map(chatId => ({ [chatId]: unalertedMessagesByChatId[chatId] })));
// Mark messages as alerted
const newLastAlertTimestampByChat = { ...lastAlertTimestampByChat };
chatsWithPendingAlerts.forEach(
(chatId) => {
newLastAlertTimestampByChat[chatId] = lastUnalertedMessageTimestampByChat[chatId];
},
);
2019-04-10 01:04:33 +08:00
this.setChatMessagesState(newPendingNotificationsByChat, newLastAlertTimestampByChat);
2019-01-14 21:23:35 +08:00
}
2019-01-16 21:08:20 +08:00
setAlertEnabledTimestamp(newAlertEnabledTimestamp) {
2019-01-15 00:23:11 +08:00
const { alertEnabledTimestamp } = this.state;
2019-01-16 21:08:20 +08:00
if (newAlertEnabledTimestamp > 0 && alertEnabledTimestamp !== newAlertEnabledTimestamp) {
this.setState({ alertEnabledTimestamp: newAlertEnabledTimestamp });
2019-01-14 21:23:35 +08:00
}
}
2019-01-14 21:23:35 +08:00
setChatMessagesState(pendingNotificationsByChat, lastAlertTimestampByChat) {
this.setState({ pendingNotificationsByChat, lastAlertTimestampByChat });
}
mapContentText(message) {
const {
intl,
} = this.props;
const contentMessage = message
.map((content) => {
if (content.text === 'PUBLIC_CHAT_CLEAR') return intl.formatMessage(intlMessages.publicChatClear);
2019-01-14 21:23:35 +08:00
/* this code is to remove html tags that come in the server's messages */
const tempDiv = document.createElement('div');
tempDiv.innerHTML = content.text;
const textWithoutTag = tempDiv.innerText;
return textWithoutTag;
});
2019-01-14 21:23:35 +08:00
return contentMessage;
}
createMessage(name, message) {
return (
<div className={styles.pushMessageContent}>
<h3 className={styles.userNameMessage}>{name}</h3>
<div className={styles.contentMessage}>
{
this.mapContentText(message)
2019-01-14 21:23:35 +08:00
.reduce((acc, text) => [...acc, (<br key={_.uniqueId('br_')} />), text], [])
}
</div>
</div>
);
}
2019-01-14 21:23:35 +08:00
render() {
const {
2019-01-14 21:23:35 +08:00
audioAlertDisabled,
idChatOpen,
2019-01-14 21:23:35 +08:00
pushAlertDisabled,
intl,
activeChats,
} = this.props;
const {
2019-01-14 21:23:35 +08:00
pendingNotificationsByChat,
} = this.state;
const notCurrentTabOrMinimized = document.hidden;
const hasPendingNotifications = Object.keys(pendingNotificationsByChat).length > 0;
const unreadMessages = activeChats.reduce((a, b) => a + b.unreadCounter, 0);
const shouldPlayChatAlert = (notCurrentTabOrMinimized && unreadMessages > 0)
|| (hasPendingNotifications && !idChatOpen);
return (
2019-04-10 01:04:33 +08:00
<Fragment>
{
!audioAlertDisabled || (!audioAlertDisabled && notCurrentTabOrMinimized)
? <ChatAudioAlert play={shouldPlayChatAlert} />
: null
}
{
2019-01-14 21:23:35 +08:00
!pushAlertDisabled
? Object.keys(pendingNotificationsByChat)
.map((chatId) => {
// Only display the latest group of messages (up to 5 messages)
const reducedMessage = Service
.reduceAndMapGroupMessages(pendingNotificationsByChat[chatId].slice(-5)).pop();
if (!reducedMessage || !reducedMessage.sender) return null;
2019-01-14 21:23:35 +08:00
const content = this
.createMessage(reducedMessage.sender.name, reducedMessage.content);
return (
<ChatPushAlert
key={chatId}
chatId={chatId}
content={content}
title={
(chatId === 'MAIN-PUBLIC-GROUP-CHAT')
? <span>{intl.formatMessage(intlMessages.appToastChatPublic)}</span>
: <span>{intl.formatMessage(intlMessages.appToastChatPrivate)}</span>
}
onOpen={
() => {
let pendingNotifications = pendingNotificationsByChat;
delete pendingNotifications[chatId];
pendingNotifications = { ...pendingNotifications };
this.setState({ pendingNotificationsByChat: pendingNotifications });
}}
alertDuration={ALERT_DURATION}
/>
);
})
: null
}
2019-04-10 01:04:33 +08:00
</Fragment>
);
}
}
ChatAlert.propTypes = propTypes;
export default injectIntl(ChatAlert);