bigbluebutton-Github/bigbluebutton-html5/imports/ui/components/polling/component.jsx

344 lines
11 KiB
React
Raw Normal View History

2018-10-24 22:17:13 +08:00
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import injectWbResizeEvent from '/imports/ui/components/presentation/resize-wrapper/component';
import { defineMessages, injectIntl } from 'react-intl';
import { Meteor } from 'meteor/meteor';
2021-11-05 01:03:25 +08:00
import Styled from './styles';
import AudioService from '/imports/ui/components/audio/service';
import Checkbox from '/imports/ui/components/common/checkbox/component';
2016-05-06 02:50:18 +08:00
2021-05-01 22:59:20 +08:00
const MAX_INPUT_CHARS = Meteor.settings.public.poll.maxTypedAnswerLength;
2017-04-25 10:08:18 +08:00
const intlMessages = defineMessages({
pollingTitleLabel: {
id: 'app.polling.pollingTitle',
},
pollAnswerLabel: {
id: 'app.polling.pollAnswerLabel',
},
pollAnswerDesc: {
id: 'app.polling.pollAnswerDesc',
2017-04-25 10:08:18 +08:00
},
pollQuestionTitle: {
id: 'app.polling.pollQuestionTitle',
},
responseIsSecret: {
id: 'app.polling.responseSecret',
},
responseNotSecret: {
id: 'app.polling.responseNotSecret',
},
submitLabel: {
id: 'app.polling.submitLabel',
},
submitAriaLabel: {
id: 'app.polling.submitAriaLabel',
},
responsePlaceholder: {
id: 'app.polling.responsePlaceholder',
2021-02-05 01:55:18 +08:00
},
2017-04-25 10:08:18 +08:00
});
const validateInput = (i) => {
let _input = i;
if (/^\s/.test(_input)) _input = '';
return _input;
};
2018-10-24 22:17:13 +08:00
class Polling extends Component {
constructor(props) {
super(props);
2021-02-05 01:55:18 +08:00
this.state = {
typedAns: '',
checkedAnswers: [],
};
2021-02-05 01:55:18 +08:00
this.pollingContainer = null;
2018-10-24 22:17:13 +08:00
this.play = this.play.bind(this);
this.handleUpdateResponseInput = this.handleUpdateResponseInput.bind(this);
2021-02-05 01:55:18 +08:00
this.renderButtonAnswers = this.renderButtonAnswers.bind(this);
this.handleCheckboxChange = this.handleCheckboxChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.renderCheckboxAnswers = this.renderCheckboxAnswers.bind(this);
this.handleMessageKeyDown = this.handleMessageKeyDown.bind(this);
2018-10-24 22:17:13 +08:00
}
componentDidMount() {
this.play();
this.pollingContainer && this.pollingContainer?.focus();
2018-10-24 22:17:13 +08:00
}
play() {
AudioService.playAlertSound(`${Meteor.settings.public.app.cdn
2020-12-01 00:09:35 +08:00
+ Meteor.settings.public.app.basename
+ Meteor.settings.public.app.instanceId}`
+ '/resources/sounds/Poll.mp3');
2018-10-24 22:17:13 +08:00
}
handleUpdateResponseInput(e) {
this.responseInput.value = validateInput(e.target.value);
this.setState({ typedAns: this.responseInput.value });
}
2021-03-08 01:26:10 +08:00
handleSubmit(pollId) {
const { handleVote } = this.props;
const { checkedAnswers } = this.state;
handleVote(pollId, checkedAnswers);
}
handleCheckboxChange(pollId, answerId) {
const { checkedAnswers } = this.state;
if (checkedAnswers.includes(answerId)) {
checkedAnswers.splice(checkedAnswers.indexOf(answerId), 1);
} else {
checkedAnswers.push(answerId);
}
checkedAnswers.sort();
this.setState({ checkedAnswers });
}
handleMessageKeyDown(e) {
const {
poll,
handleTypedVote,
} = this.props;
const {
typedAns,
} = this.state;
if (e.keyCode === 13 && typedAns.length > 0) {
handleTypedVote(poll.pollId, typedAns);
}
}
2021-11-05 01:03:25 +08:00
renderButtonAnswers() {
2021-02-05 01:55:18 +08:00
const {
isMeteorConnected,
intl,
poll,
handleVote,
handleTypedVote,
2021-02-05 01:55:18 +08:00
pollAnswerIds,
pollTypes,
isDefaultPoll,
2021-02-05 01:55:18 +08:00
} = this.props;
2021-05-02 04:50:24 +08:00
const {
typedAns,
} = this.state;
if (!poll) return null;
2021-11-05 01:03:25 +08:00
const { stackOptions, answers, question, pollType } = poll;
const defaultPoll = isDefaultPoll(pollType);
2021-03-07 22:12:49 +08:00
return (
2021-09-21 20:19:39 +08:00
<div>
{
poll.pollType !== pollTypes.Response && (
<span>
{
question.length === 0 && (
2021-11-05 01:03:25 +08:00
<Styled.PollingTitle>
{intl.formatMessage(intlMessages.pollingTitleLabel)}
2021-11-05 01:03:25 +08:00
</Styled.PollingTitle>
)
}
2021-11-05 01:03:25 +08:00
<Styled.PollingAnswers removeColumns={answers.length === 1} stacked={stackOptions}>
2021-09-21 20:19:39 +08:00
{answers.map((pollAnswer) => {
2022-10-13 01:45:33 +08:00
const formattedMessageIndex = pollAnswer?.key?.toLowerCase();
let label = pollAnswer.key;
if ((defaultPoll || pollType.includes('CUSTOM')) && pollAnswerIds[formattedMessageIndex]) {
label = intl.formatMessage(pollAnswerIds[formattedMessageIndex]);
}
return (
2021-11-05 01:03:25 +08:00
<Styled.PollButtonWrapper key={pollAnswer.id} >
<Styled.PollingButton
disabled={!isMeteorConnected}
color="primary"
size="md"
label={label}
key={pollAnswer.key}
2021-09-21 20:19:39 +08:00
onClick={() => handleVote(poll.pollId, [pollAnswer.id])}
aria-labelledby={`pollAnswerLabel${pollAnswer.key}`}
aria-describedby={`pollAnswerDesc${pollAnswer.key}`}
data-test="pollAnswerOption"
/>
2021-11-05 01:03:25 +08:00
<Styled.Hidden id={`pollAnswerLabel${pollAnswer.key}`}>
{intl.formatMessage(intlMessages.pollAnswerLabel, { 0: label })}
2021-11-05 01:03:25 +08:00
</Styled.Hidden>
<Styled.Hidden id={`pollAnswerDesc${pollAnswer.key}`}>
{intl.formatMessage(intlMessages.pollAnswerDesc, { 0: label })}
2021-11-05 01:03:25 +08:00
</Styled.Hidden>
</Styled.PollButtonWrapper>
);
})}
</Styled.PollingAnswers>
2021-05-02 04:50:24 +08:00
</span>
)
}
{
poll.pollType === pollTypes.Response
2021-05-02 04:50:24 +08:00
&& (
2021-11-05 01:03:25 +08:00
<Styled.TypedResponseWrapper>
<Styled.TypedResponseInput
2021-05-02 04:50:24 +08:00
data-test="pollAnswerOption"
onChange={(e) => {
this.handleUpdateResponseInput(e);
}}
onKeyDown={(e) => {
this.handleMessageKeyDown(e);
}}
type="text"
placeholder={intl.formatMessage(intlMessages.responsePlaceholder)}
maxLength={MAX_INPUT_CHARS}
ref={(r) => { this.responseInput = r; }}
2022-11-04 00:06:25 +08:00
onPaste={(e) => { e.stopPropagation(); }}
2022-11-04 03:24:00 +08:00
onCut={(e) => { e.stopPropagation(); }}
onCopy={(e) => { e.stopPropagation(); }}
2021-05-02 04:50:24 +08:00
/>
2021-11-05 01:03:25 +08:00
<Styled.SubmitVoteButton
2021-05-02 04:50:24 +08:00
data-test="submitAnswer"
disabled={typedAns.length === 0}
color="primary"
size="sm"
label={intl.formatMessage(intlMessages.submitLabel)}
aria-label={intl.formatMessage(intlMessages.submitAriaLabel)}
onClick={() => {
handleTypedVote(poll.pollId, typedAns);
}}
/>
2021-11-05 01:03:25 +08:00
</Styled.TypedResponseWrapper>
2021-05-02 04:50:24 +08:00
)
}
2021-11-05 01:03:25 +08:00
<Styled.PollingSecret>
{intl.formatMessage(poll.secretPoll ? intlMessages.responseIsSecret : intlMessages.responseNotSecret)}
2021-11-05 01:03:25 +08:00
</Styled.PollingSecret>
</div>
2021-03-07 22:12:49 +08:00
);
2021-02-05 01:55:18 +08:00
}
2021-09-22 00:04:15 +08:00
renderCheckboxAnswers() {
2021-02-05 01:55:18 +08:00
const {
isMeteorConnected,
intl,
poll,
pollAnswerIds,
} = this.props;
2021-02-08 21:23:43 +08:00
const { checkedAnswers } = this.state;
2021-03-07 22:12:49 +08:00
const { question } = poll;
2021-02-05 01:55:18 +08:00
return (
<div>
2021-03-07 22:12:49 +08:00
{question.length === 0
&& (
2021-11-05 01:03:25 +08:00
<Styled.PollingTitle>
2021-03-07 22:12:49 +08:00
{intl.formatMessage(intlMessages.pollingTitleLabel)}
2021-11-05 01:03:25 +08:00
</Styled.PollingTitle>
2021-05-02 04:50:24 +08:00
)}
2021-11-05 01:03:25 +08:00
<Styled.MultipleResponseAnswersTable>
2021-03-07 22:12:49 +08:00
{poll.answers.map((pollAnswer) => {
2022-10-13 01:45:33 +08:00
const formattedMessageIndex = pollAnswer?.key?.toLowerCase();
let label = pollAnswer?.key;
2021-03-07 22:12:49 +08:00
if (pollAnswerIds[formattedMessageIndex]) {
label = intl.formatMessage(pollAnswerIds[formattedMessageIndex]);
}
2021-03-07 22:12:49 +08:00
return (
2021-11-05 01:03:25 +08:00
<Styled.CheckboxContainer
2021-03-07 22:12:49 +08:00
key={pollAnswer.id}
>
2021-03-08 01:26:10 +08:00
<td>
2022-10-26 04:12:59 +08:00
<Styled.PollingCheckbox data-test="optionsAnswers">
2021-11-06 01:29:46 +08:00
<Checkbox
disabled={!isMeteorConnected}
id={`answerInput${pollAnswer.key}`}
onChange={() => this.handleCheckboxChange(poll.pollId, pollAnswer.id)}
checked={checkedAnswers.includes(pollAnswer.id)}
ariaLabelledBy={`pollAnswerLabel${pollAnswer.key}`}
ariaDescribedBy={`pollAnswerDesc${pollAnswer.key}`}
/>
</Styled.PollingCheckbox>
2021-03-08 01:26:10 +08:00
</td>
2021-11-05 01:03:25 +08:00
<Styled.MultipleResponseAnswersTableAnswerText>
2021-03-08 01:26:10 +08:00
<label id={`pollAnswerLabel${pollAnswer.key}`}>
{label}
</label>
2021-11-05 01:03:25 +08:00
<Styled.Hidden id={`pollAnswerDesc${pollAnswer.key}`} >
2021-03-08 01:26:10 +08:00
{intl.formatMessage(intlMessages.pollAnswerDesc, { 0: label })}
2021-11-05 01:03:25 +08:00
</Styled.Hidden>
</Styled.MultipleResponseAnswersTableAnswerText>
</Styled.CheckboxContainer>
2021-03-07 22:12:49 +08:00
);
})}
2021-11-05 01:03:25 +08:00
</Styled.MultipleResponseAnswersTable>
2021-02-05 01:55:18 +08:00
<div>
2021-11-05 01:03:25 +08:00
<Styled.SubmitVoteButton
2021-02-08 21:23:58 +08:00
disabled={!isMeteorConnected || checkedAnswers.length === 0}
color="primary"
2021-03-07 22:12:49 +08:00
size="sm"
label={intl.formatMessage(intlMessages.submitLabel)}
aria-label={intl.formatMessage(intlMessages.submitAriaLabel)}
onClick={() => this.handleSubmit(poll.pollId)}
2022-10-26 04:12:59 +08:00
data-test="submitAnswersMultiple"
2021-02-05 01:55:18 +08:00
/>
</div>
</div>
);
2021-02-05 01:55:18 +08:00
}
2018-10-24 22:17:13 +08:00
render() {
2019-05-23 02:00:44 +08:00
const {
intl,
poll,
2019-05-23 02:00:44 +08:00
} = this.props;
if (!poll) return null;
2021-11-05 01:03:25 +08:00
const { stackOptions, question } = poll;
2018-10-24 22:17:13 +08:00
return (
2021-11-05 01:03:25 +08:00
<Styled.Overlay>
<Styled.PollingContainer
autoWidth={stackOptions}
data-test="pollingContainer"
role="complementary"
ref={el => this.pollingContainer = el}
tabIndex={-1}
>
2021-05-02 04:50:24 +08:00
{
question.length > 0 && (
2021-11-05 01:03:25 +08:00
<Styled.QHeader>
<Styled.QTitle>
2021-09-21 20:19:39 +08:00
{intl.formatMessage(intlMessages.pollQuestionTitle)}
2021-11-05 01:03:25 +08:00
</Styled.QTitle>
<Styled.QText data-test="pollQuestion">{question}</Styled.QText>
</Styled.QHeader>
2021-05-02 04:50:24 +08:00
)
}
2021-11-05 01:03:25 +08:00
{poll.isMultipleResponse ? this.renderCheckboxAnswers() : this.renderButtonAnswers()}
</Styled.PollingContainer>
</Styled.Overlay>
2021-05-01 22:59:20 +08:00
);
2018-10-24 22:17:13 +08:00
}
}
2017-04-25 10:08:18 +08:00
export default injectIntl(injectWbResizeEvent(Polling));
Polling.propTypes = {
intl: PropTypes.shape({
formatMessage: PropTypes.func.isRequired,
}).isRequired,
handleVote: PropTypes.func.isRequired,
handleTypedVote: PropTypes.func.isRequired,
poll: PropTypes.shape({
pollId: PropTypes.string.isRequired,
2018-03-13 00:29:22 +08:00
answers: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.number.isRequired,
2021-11-27 00:30:56 +08:00
key: PropTypes.string,
2018-03-13 00:29:22 +08:00
}).isRequired).isRequired,
}).isRequired,
};