bigbluebutton-Github/bigbluebutton-html5/imports/ui/components/poll/live-result/component.jsx

286 lines
8.2 KiB
React
Raw Normal View History

import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
2018-10-16 03:05:50 +08:00
import { defineMessages, injectIntl } from 'react-intl';
import caseInsensitiveReducer from '/imports/utils/caseInsensitiveReducer';
import { Session } from 'meteor/session';
2021-11-04 20:49:04 +08:00
import Styled from './styles';
2018-11-02 02:20:35 +08:00
import Service from './service';
2021-11-04 20:49:04 +08:00
import Settings from '/imports/ui/services/settings';
2023-02-23 22:23:51 +08:00
import { uniqueId } from '/imports/utils/string-utils';
2018-10-10 23:49:58 +08:00
2018-10-16 03:05:50 +08:00
const intlMessages = defineMessages({
usersTitle: {
id: 'app.poll.liveResult.usersTitle',
description: 'heading label for poll users',
},
responsesTitle: {
id: 'app.poll.liveResult.responsesTitle',
description: 'heading label for poll responses',
},
2018-10-24 22:17:13 +08:00
publishLabel: {
id: 'app.poll.publishLabel',
description: 'label for the publish button',
},
cancelPollLabel: {
id: 'app.poll.cancelPollLabel',
description: 'label for cancel poll button',
},
2018-10-24 22:17:13 +08:00
backLabel: {
id: 'app.poll.backLabel',
description: 'label for the return to poll options button',
},
2019-04-18 00:36:04 +08:00
doneLabel: {
id: 'app.createBreakoutRoom.doneLabel',
description: 'label shown when all users have responded',
},
waitingLabel: {
id: 'app.poll.waitingLabel',
description: 'label shown while waiting for responses',
},
secretPollLabel: {
id: 'app.poll.liveResult.secretLabel',
description: 'label shown instead of users in poll responses if poll is secret',
},
2018-10-16 03:05:50 +08:00
});
2019-04-18 00:36:04 +08:00
const getResponseString = (obj) => {
const { children } = obj.props;
if (typeof children !== 'string') {
return getResponseString(children[1]);
}
2019-04-18 00:36:04 +08:00
return children;
};
class LiveResult extends PureComponent {
static getDerivedStateFromProps(nextProps) {
2019-05-23 02:00:44 +08:00
const {
currentPoll, intl, pollAnswerIds, usernames, isDefaultPoll,
2019-05-23 02:00:44 +08:00
} = nextProps;
if (!currentPoll) return null;
const {
answers, responses, users, numResponders, pollType
} = currentPoll;
const defaultPoll = isDefaultPoll(pollType);
2021-03-19 00:46:49 +08:00
const currentPollQuestion = (currentPoll.question) ? currentPoll.question : '';
let userAnswers = responses
2021-09-17 22:03:09 +08:00
? [...users, ...responses.map(u => u.userId)]
: [...users];
userAnswers = userAnswers.map(id => usernames[id])
.map((user) => {
let answer = '';
if (responses) {
2021-09-17 22:03:09 +08:00
const response = responses.find(r => r.userId === user.userId);
2021-02-05 00:54:50 +08:00
if (response) {
const formattedAnswers = [];
2021-02-05 00:54:50 +08:00
response.answerIds.forEach((answerId) => {
const formattedMessageIndex = answers[answerId]?.key?.toLowerCase();
const formattedAnswer = defaultPoll && pollAnswerIds[formattedMessageIndex]
? intl.formatMessage(pollAnswerIds[formattedMessageIndex])
: answers[answerId].key;
formattedAnswers.push(formattedAnswer);
});
answer = formattedAnswers.join(', ');
2021-02-05 00:54:50 +08:00
}
}
return {
name: user.name,
answer,
};
})
2018-11-02 02:20:35 +08:00
.sort(Service.sortUsers)
2019-05-23 02:00:44 +08:00
.reduce((acc, user) => {
return ([
...acc,
(
2023-02-23 22:23:51 +08:00
<tr key={uniqueId('stats-')}>
2021-11-04 20:49:04 +08:00
<Styled.ResultLeft>{user.name}</Styled.ResultLeft>
<Styled.ResultRight data-test="receivedAnswer">
{user.answer}
2021-11-04 20:49:04 +08:00
</Styled.ResultRight>
2019-05-23 02:00:44 +08:00
</tr>
),
]);
}, []);
2018-10-10 23:49:58 +08:00
const pollStats = [];
answers.reduce(caseInsensitiveReducer, []).map((obj) => {
const formattedMessageIndex = obj?.key?.toLowerCase();
const pct = Math.round(obj.numVotes / numResponders * 100);
const pctFotmatted = `${Number.isNaN(pct) ? 0 : pct}%`;
const calculatedWidth = {
width: pctFotmatted,
};
2018-10-31 21:28:32 +08:00
2018-12-18 23:15:51 +08:00
return pollStats.push(
2023-02-23 22:23:51 +08:00
<Styled.Main key={uniqueId('stats-')}>
2021-11-04 20:49:04 +08:00
<Styled.Left>
2019-05-23 02:00:44 +08:00
{
defaultPoll && pollAnswerIds[formattedMessageIndex]
2019-05-23 02:00:44 +08:00
? intl.formatMessage(pollAnswerIds[formattedMessageIndex])
: obj.key
}
2021-11-04 20:49:04 +08:00
</Styled.Left>
<Styled.Center>
<Styled.BarShade style={calculatedWidth} />
2022-10-26 04:12:59 +08:00
<Styled.BarVal data-test="numberOfVotes">{obj.numVotes || 0}</Styled.BarVal>
2021-11-04 20:49:04 +08:00
</Styled.Center>
<Styled.Right>
{pctFotmatted}
2021-11-04 20:49:04 +08:00
</Styled.Right>
</Styled.Main>,
2018-12-18 23:15:51 +08:00
);
2018-10-31 21:28:32 +08:00
});
2018-10-10 23:49:58 +08:00
return {
userAnswers,
pollStats,
2021-03-19 00:46:49 +08:00
currentPollQuestion,
};
}
constructor(props) {
super(props);
this.state = {
userAnswers: null,
pollStats: null,
2021-03-19 00:46:49 +08:00
currentPollQuestion: null,
};
2018-10-10 23:49:58 +08:00
}
render() {
2018-10-24 22:17:13 +08:00
const {
isMeteorConnected,
intl,
stopPoll,
handleBackClick,
currentPoll,
2018-10-24 22:17:13 +08:00
} = this.props;
2018-10-16 03:05:50 +08:00
2021-03-19 00:46:49 +08:00
const { userAnswers, pollStats, currentPollQuestion } = this.state;
2021-11-04 20:49:04 +08:00
const { animations } = Settings.application;
2019-04-18 00:36:04 +08:00
let waiting;
let userCount = 0;
let respondedCount = 0;
if (userAnswers) {
userCount = userAnswers.length;
userAnswers.map((user) => {
const response = getResponseString(user);
if (response === '') return user;
2019-04-18 00:36:04 +08:00
respondedCount += 1;
return user;
});
waiting = respondedCount !== userAnswers.length && currentPoll;
2019-04-18 00:36:04 +08:00
}
2018-10-10 23:49:58 +08:00
return (
<div>
2021-11-04 20:49:04 +08:00
<Styled.Stats>
2022-10-26 04:12:59 +08:00
{currentPollQuestion ? <Styled.Title data-test="currentPollQuestion">{currentPollQuestion}</Styled.Title> : null}
2021-11-04 20:49:04 +08:00
<Styled.Status>
2020-09-22 06:52:38 +08:00
{waiting
? (
<span>
{`${intl.formatMessage(intlMessages.waitingLabel, {
0: respondedCount,
1: userCount,
})} `}
</span>
)
: <span>{intl.formatMessage(intlMessages.doneLabel)}</span>}
{waiting
2021-11-04 20:49:04 +08:00
? <Styled.ConnectingAnimation animations={animations}/> : null}
</Styled.Status>
{pollStats}
2021-11-04 20:49:04 +08:00
</Styled.Stats>
{currentPoll && currentPoll.answers.length >= 0
? (
2021-11-04 20:49:04 +08:00
<Styled.ButtonsActions>
<Styled.PublishButton
disabled={!isMeteorConnected}
onClick={() => {
Session.set('pollInitiated', false);
Service.publishPoll();
stopPoll();
}}
label={intl.formatMessage(intlMessages.publishLabel)}
data-test="publishPollingLabel"
color="primary"
/>
2021-11-04 20:49:04 +08:00
<Styled.CancelButton
disabled={!isMeteorConnected}
onClick={() => {
Session.set('pollInitiated', false);
Session.set('resetPollPanel', true);
stopPoll();
}}
label={intl.formatMessage(intlMessages.cancelPollLabel)}
data-test="cancelPollLabel"
/>
2021-11-04 20:49:04 +08:00
</Styled.ButtonsActions>
) : (
2021-11-04 20:49:04 +08:00
<Styled.LiveResultButton
disabled={!isMeteorConnected}
onClick={() => {
handleBackClick();
}}
label={intl.formatMessage(intlMessages.backLabel)}
color="primary"
2021-08-17 10:04:17 +08:00
data-test="restartPoll"
/>
2021-09-17 22:03:09 +08:00
)
}
2021-11-04 20:49:04 +08:00
<Styled.Separator />
{ currentPoll && !currentPoll.secretPoll
? (
<table>
<tbody>
<tr>
2021-11-04 20:49:04 +08:00
<Styled.THeading>{intl.formatMessage(intlMessages.usersTitle)}</Styled.THeading>
<Styled.THeading>{intl.formatMessage(intlMessages.responsesTitle)}</Styled.THeading>
</tr>
{userAnswers}
</tbody>
</table>
) : (
currentPoll ? (<div>{intl.formatMessage(intlMessages.secretPollLabel)}</div>) : null
)}
2018-10-10 23:49:58 +08:00
</div>
);
}
}
2018-10-16 03:05:50 +08:00
export default injectIntl(LiveResult);
LiveResult.defaultProps = { currentPoll: null };
LiveResult.propTypes = {
intl: PropTypes.shape({
formatMessage: PropTypes.func.isRequired,
}).isRequired,
currentPoll: PropTypes.oneOfType([
PropTypes.arrayOf(Object),
PropTypes.shape({
answers: PropTypes.arrayOf(PropTypes.object),
users: PropTypes.arrayOf(PropTypes.string),
}),
]),
2018-12-18 23:15:51 +08:00
stopPoll: PropTypes.func.isRequired,
handleBackClick: PropTypes.func.isRequired,
};