Merge pull request #10195 from KDSBrowne/dev2.3-fix-poll-chat-grouping
Improve poll results in public chat
This commit is contained in:
commit
9bdaf3960b
@ -4,6 +4,7 @@ import { FormattedTime, defineMessages, injectIntl } from 'react-intl';
|
||||
import _ from 'lodash';
|
||||
import Icon from '/imports/ui/components/icon/component';
|
||||
import UserAvatar from '/imports/ui/components/user-avatar/component';
|
||||
import cx from 'classnames';
|
||||
import Message from './message/component';
|
||||
|
||||
import { styles } from './styles';
|
||||
@ -42,10 +43,6 @@ const intlMessages = defineMessages({
|
||||
id: 'app.chat.pollResult',
|
||||
description: 'used in place of user name who published poll to chat',
|
||||
},
|
||||
legendTitle: {
|
||||
id: 'app.polling.pollingTitle',
|
||||
description: 'heading for chat poll legend',
|
||||
},
|
||||
});
|
||||
|
||||
class MessageListItem extends Component {
|
||||
@ -111,13 +108,14 @@ class MessageListItem extends Component {
|
||||
handleReadMessage,
|
||||
scrollArea,
|
||||
intl,
|
||||
chats,
|
||||
messages,
|
||||
} = this.props;
|
||||
|
||||
if (chats.length < 1) return null;
|
||||
if (messages && messages[0].text.includes('bbb-published-poll-<br/>')) {
|
||||
return this.renderPollItem();
|
||||
}
|
||||
|
||||
const dateTime = new Date(time);
|
||||
|
||||
const regEx = /<a[^>]+>/i;
|
||||
|
||||
return (
|
||||
@ -149,7 +147,7 @@ class MessageListItem extends Component {
|
||||
</time>
|
||||
</div>
|
||||
<div className={styles.messages} data-test="chatUserMessage">
|
||||
{chats.map(message => (
|
||||
{messages.map(message => (
|
||||
<Message
|
||||
className={(regEx.test(message.text) ? styles.hyperlink : styles.message)}
|
||||
key={message.id}
|
||||
@ -173,53 +171,17 @@ class MessageListItem extends Component {
|
||||
user,
|
||||
time,
|
||||
intl,
|
||||
polls,
|
||||
isDefaultPoll,
|
||||
messages,
|
||||
scrollArea,
|
||||
chatAreaId,
|
||||
lastReadMessageTime,
|
||||
handleReadMessage,
|
||||
} = this.props;
|
||||
|
||||
if (polls.length < 1) return null;
|
||||
|
||||
const dateTime = new Date(time);
|
||||
|
||||
let pollText = [];
|
||||
const pollElement = [];
|
||||
const legendElements = [
|
||||
(<div
|
||||
className={styles.optionsTitle}
|
||||
key={_.uniqueId('chat-poll-options-')}
|
||||
>
|
||||
{intl.formatMessage(intlMessages.legendTitle)}
|
||||
</div>),
|
||||
];
|
||||
|
||||
let isDefault = true;
|
||||
polls.forEach((poll) => {
|
||||
isDefault = isDefaultPoll(poll.text);
|
||||
pollText = poll.text.split('<br/>');
|
||||
pollElement.push(pollText.map((p, index) => {
|
||||
if (!isDefault) {
|
||||
legendElements.push(
|
||||
<div key={_.uniqueId('chat-poll-legend-')} className={styles.pollLegend}>
|
||||
<span>{`${index + 1}: `}</span>
|
||||
<span className={styles.pollOption}>{p.split(':')[0]}</span>
|
||||
</div>,
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={_.uniqueId('chat-poll-result-')} className={styles.pollLine}>
|
||||
{!isDefault ? p.replace(p.split(':')[0], index + 1) : p}
|
||||
</div>
|
||||
);
|
||||
}));
|
||||
});
|
||||
|
||||
if (!isDefault) {
|
||||
pollElement.push(<div key={_.uniqueId('chat-poll-separator-')} className={styles.divider} />);
|
||||
pollElement.push(legendElements);
|
||||
}
|
||||
|
||||
return polls ? (
|
||||
return messages ? (
|
||||
<div className={styles.item} key={_.uniqueId('message-poll-item-')}>
|
||||
<div className={styles.wrapper} ref={(ref) => { this.item = ref; }}>
|
||||
<div className={styles.avatarWrapper}>
|
||||
@ -240,15 +202,19 @@ class MessageListItem extends Component {
|
||||
<FormattedTime value={dateTime} />
|
||||
</time>
|
||||
</div>
|
||||
<div className={styles.messages}>
|
||||
{polls[0] ? (
|
||||
<div className={styles.pollWrapper} style={{ borderLeft: `3px ${user.color} solid` }}>
|
||||
{
|
||||
pollElement
|
||||
}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<Message
|
||||
type="poll"
|
||||
className={cx(styles.message, styles.pollWrapper)}
|
||||
key={messages[0].id}
|
||||
text={messages[0].text}
|
||||
time={messages[0].time}
|
||||
chatAreaId={chatAreaId}
|
||||
lastReadMessageTime={lastReadMessageTime}
|
||||
handleReadMessage={handleReadMessage}
|
||||
scrollArea={scrollArea}
|
||||
color={user.color}
|
||||
isDefaultPoll={isDefaultPoll(messages[0].text.replace('bbb-published-poll-<br/>', ''))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -266,10 +232,7 @@ class MessageListItem extends Component {
|
||||
|
||||
return (
|
||||
<div className={styles.item}>
|
||||
{[
|
||||
this.renderPollItem(),
|
||||
this.renderMessageItem(),
|
||||
]}
|
||||
{this.renderMessageItem()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -15,26 +15,10 @@ export default withTracker(({ message }) => {
|
||||
const mappedMessage = ChatService.mapGroupMessage(message);
|
||||
const messages = mappedMessage.content;
|
||||
|
||||
const chats = [];
|
||||
const polls = [];
|
||||
|
||||
if (messages.length > 0) {
|
||||
messages.forEach((m) => {
|
||||
if (m.text.includes('bbb-published-poll-<br/>')) {
|
||||
m.text = m.text.replace(/^bbb-published-poll-<br\/>/g, '');
|
||||
polls.push(m);
|
||||
} else {
|
||||
chats.push(m);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
messages,
|
||||
user: mappedMessage.sender,
|
||||
time: mappedMessage.time,
|
||||
chats,
|
||||
polls,
|
||||
isDefaultPoll: (pollText) => {
|
||||
const pollValue = pollText.replace(/<br\/>|[ :|%\n\d+]/g, '');
|
||||
switch (pollValue) {
|
||||
|
@ -2,6 +2,7 @@ import React, { PureComponent } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import _ from 'lodash';
|
||||
import fastdom from 'fastdom';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
const propTypes = {
|
||||
text: PropTypes.string.isRequired,
|
||||
@ -34,13 +35,22 @@ const isElementInViewport = (el) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default class MessageListItem extends PureComponent {
|
||||
const intlMessages = defineMessages({
|
||||
legendTitle: {
|
||||
id: 'app.polling.pollingTitle',
|
||||
description: 'heading for chat poll legend',
|
||||
},
|
||||
});
|
||||
|
||||
class MessageListItem extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.ticking = false;
|
||||
|
||||
this.handleMessageInViewport = _.debounce(this.handleMessageInViewport.bind(this), 50);
|
||||
|
||||
this.renderPollListItem = this.renderPollListItem.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@ -145,17 +155,56 @@ export default class MessageListItem extends PureComponent {
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
renderPollListItem() {
|
||||
const {
|
||||
intl,
|
||||
text,
|
||||
className,
|
||||
color,
|
||||
isDefaultPoll,
|
||||
} = this.props;
|
||||
|
||||
const formatBoldBlack = s => s.bold().fontcolor('black');
|
||||
|
||||
let _text = text.replace('bbb-published-poll-<br/>', '');
|
||||
|
||||
if (!isDefaultPoll) {
|
||||
const entries = _text.split('<br/>');
|
||||
const options = [];
|
||||
entries.map((e) => { options.push([e.slice(0, e.indexOf(':'))]); return e; });
|
||||
options.map((o, idx) => {
|
||||
_text = formatBoldBlack(_text.replace(o, idx + 1));
|
||||
return _text;
|
||||
});
|
||||
_text += formatBoldBlack(`<br/><br/>${intl.formatMessage(intlMessages.legendTitle)}`);
|
||||
options.map((o, idx) => { _text += `<br/>${idx + 1}: ${o}`; return _text; });
|
||||
}
|
||||
|
||||
return (
|
||||
<p
|
||||
className={className}
|
||||
style={{ borderLeft: `3px ${color} solid` }}
|
||||
ref={(ref) => { this.text = ref; }}
|
||||
dangerouslySetInnerHTML={{ __html: isDefaultPoll ? formatBoldBlack(_text) : _text }}
|
||||
data-test="chatMessageText"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
text,
|
||||
type,
|
||||
className,
|
||||
} = this.props;
|
||||
|
||||
if (type === 'poll') return this.renderPollListItem();
|
||||
|
||||
return (
|
||||
<p
|
||||
className={className}
|
||||
ref={(ref) => { this.text = ref; }}
|
||||
dangerouslySetInnerHTML={{ __html: text }}
|
||||
className={className}
|
||||
data-test="chatMessageText"
|
||||
/>
|
||||
);
|
||||
@ -164,3 +213,5 @@ export default class MessageListItem extends PureComponent {
|
||||
|
||||
MessageListItem.propTypes = propTypes;
|
||||
MessageListItem.defaultProps = defaultProps;
|
||||
|
||||
export default injectIntl(MessageListItem);
|
||||
|
@ -4,7 +4,7 @@
|
||||
--systemMessage-background-color: #F9FBFC;
|
||||
--systemMessage-border-color: #C5CDD4;
|
||||
--systemMessage-font-color: var(--color-dark-grey);
|
||||
--chat-poll-margin-sm: .25rem;
|
||||
--chat-poll-margin-sm: .5rem;
|
||||
}
|
||||
|
||||
.item {
|
||||
@ -159,42 +159,11 @@
|
||||
bottom: var(--border-size-large);
|
||||
}
|
||||
|
||||
.pollLine {
|
||||
overflow-wrap: break-word;
|
||||
font-size: var(--font-size-large);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.pollWrapper {
|
||||
background: var(--systemMessage-background-color);
|
||||
border: solid 1px var(--color-gray-lighter);
|
||||
border-radius: var(--border-radius);
|
||||
padding: var(--chat-poll-margin-sm);
|
||||
padding-left: 1rem;
|
||||
margin-top: .5rem;
|
||||
background: var(--systemMessage-background-color);
|
||||
}
|
||||
|
||||
.pollLegend {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-top: var(--chat-poll-margin-sm);
|
||||
}
|
||||
|
||||
.pollOption {
|
||||
word-break: break-word;
|
||||
margin-left: var(--md-padding-y);
|
||||
}
|
||||
|
||||
.optionsTitle {
|
||||
font-weight: bold;
|
||||
margin-top: var(--md-padding-y);
|
||||
}
|
||||
|
||||
.divider {
|
||||
position: relative;
|
||||
height: 1px;
|
||||
width: 95%;
|
||||
margin-right: auto;
|
||||
margin-top: var(--md-padding-y);
|
||||
border-bottom: solid 1px var(--color-gray-lightest);
|
||||
margin-top: var(--chat-poll-margin-sm) !important;
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ const UnsentMessagesCollection = new Mongo.Collection(null);
|
||||
// session for closed chat list
|
||||
const CLOSED_CHAT_LIST_KEY = 'closedChatList';
|
||||
|
||||
const POLL_MESSAGE_PREFIX = 'bbb-published-poll-<br/>';
|
||||
|
||||
const getUser = userId => Users.findOne({ userId });
|
||||
|
||||
const getWelcomeProp = () => Meetings.findOne({ meetingId: Auth.meetingID },
|
||||
@ -83,11 +85,15 @@ const reduceGroupMessages = (previous, current) => {
|
||||
return previous.concat(currentMessage);
|
||||
}
|
||||
// Check if the last message is from the same user and time discrepancy
|
||||
// between the two messages exceeds window and then group current message
|
||||
// with the last one
|
||||
// between the two messages exceeds window and then group current
|
||||
// message with the last one
|
||||
const timeOfLastMessage = lastMessage.content[lastMessage.content.length - 1].time;
|
||||
const isOrWasPoll = currentMessage.message.includes(POLL_MESSAGE_PREFIX)
|
||||
|| lastMessage.message.includes(POLL_MESSAGE_PREFIX);
|
||||
const groupingWindow = isOrWasPoll ? 0 : GROUPING_MESSAGES_WINDOW;
|
||||
|
||||
if (lastMessage.sender === currentMessage.sender
|
||||
&& (currentMessage.timestamp - timeOfLastMessage) <= GROUPING_MESSAGES_WINDOW) {
|
||||
&& (currentMessage.timestamp - timeOfLastMessage) <= groupingWindow) {
|
||||
lastMessage.content.push(currentMessage.content.pop());
|
||||
return previous;
|
||||
}
|
||||
|
@ -194,12 +194,15 @@ class LiveResult extends PureComponent {
|
||||
Session.set('pollInitiated', false);
|
||||
Service.publishPoll();
|
||||
const { answers, numRespondents } = currentPoll;
|
||||
|
||||
let responded = 0;
|
||||
let resultString = 'bbb-published-poll-\n';
|
||||
answers.forEach((item) => {
|
||||
const pct = Math.round(item.numVotes / numRespondents * 100);
|
||||
answers.map((item) => {
|
||||
responded += item.numVotes;
|
||||
return item;
|
||||
}).map((item) => {
|
||||
const numResponded = responded === numRespondents ? numRespondents : responded;
|
||||
const pct = Math.round(item.numVotes / numResponded * 100);
|
||||
const pctFotmatted = `${Number.isNaN(pct) ? 0 : pct}%`;
|
||||
|
||||
resultString += `${item.key}: ${item.numVotes || 0} | ${pctFotmatted}\n`;
|
||||
});
|
||||
|
||||
|
@ -59,7 +59,7 @@ const sendGroupMessage = (message) => {
|
||||
color: '0',
|
||||
correlationId: `${PUBLIC_CHAT_SYSTEM_ID}-${Date.now()}`,
|
||||
sender: {
|
||||
id: PUBLIC_CHAT_SYSTEM_ID,
|
||||
id: Auth.userID,
|
||||
name: '',
|
||||
},
|
||||
message,
|
||||
@ -69,9 +69,12 @@ const sendGroupMessage = (message) => {
|
||||
};
|
||||
|
||||
export default {
|
||||
amIPresenter: () => Users.findOne({ userId: Auth.userID }, { fields: { presenter: 1 } }).presenter,
|
||||
amIPresenter: () => Users.findOne(
|
||||
{ userId: Auth.userID },
|
||||
{ fields: { presenter: 1 } },
|
||||
).presenter,
|
||||
pollTypes,
|
||||
stopPoll: () => makeCall('stopPoll', Auth.userId),
|
||||
stopPoll: () => makeCall('stopPoll', Auth.userID),
|
||||
currentPoll: () => Polls.findOne({ meetingId: Auth.meetingID }),
|
||||
pollAnswerIds,
|
||||
sendGroupMessage,
|
||||
|
Loading…
Reference in New Issue
Block a user