Fix poll results and add locale entries

This commit is contained in:
Michael Zinsmeister 2021-02-08 10:57:39 +01:00
parent 723ba68549
commit eaa6f0260c
14 changed files with 117 additions and 120 deletions

View File

@ -29,7 +29,7 @@ trait StartCustomPollReqMsgHdlr extends RightsManagementTrait {
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
} else {
for {
pvo <- Polls.handleStartCustomPollReqMsg(state, msg.header.userId, msg.body.pollId, msg.body.pollType, msg.body.isMultipleChoice, msg.body.answers, liveMeeting)
pvo <- Polls.handleStartCustomPollReqMsg(state, msg.header.userId, msg.body.pollId, msg.body.pollType, msg.body.isMultipleResponse, msg.body.answers, liveMeeting)
} yield {
broadcastEvent(msg, pvo)
}

View File

@ -30,7 +30,7 @@ trait StartPollReqMsgHdlr extends RightsManagementTrait {
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
} else {
for {
pvo <- Polls.handleStartPollReqMsg(state, msg.header.userId, msg.body.pollId, msg.body.pollType, msg.body.isMultipleChoice, liveMeeting)
pvo <- Polls.handleStartPollReqMsg(state, msg.header.userId, msg.body.pollId, msg.body.pollType, msg.body.isMultipleResponse, liveMeeting)
} yield {
broadcastEvent(msg, pvo)
}

View File

@ -73,12 +73,12 @@ case class MeetingStatus(startEndTimeStatus: StartEndTimeStatus, recordingStatus
case class Meeting2x(defaultProps: DefaultProps, meetingStatus: MeetingStatus)
case class SimpleAnswerOutVO(id: Int, key: String)
case class SimplePollOutVO(id: String, isMultipleChoice: Boolean, answers: Array[SimpleAnswerOutVO])
case class SimplePollOutVO(id: String, isMultipleResponse: Boolean, answers: Array[SimpleAnswerOutVO])
case class SimpleVoteOutVO(id: Int, key: String, numVotes: Int)
case class SimplePollResultOutVO(id: String, answers: Array[SimpleVoteOutVO], numRespondents: Int, numResponders: Int)
case class Responder(userId: String, name: String)
case class AnswerVO(id: Int, key: String, text: Option[String], responders: Option[Array[Responder]])
case class QuestionVO(id: Int, questionType: String, isMultipleChoice: Boolean, questionText: Option[String], answers: Option[Array[AnswerVO]])
case class QuestionVO(id: Int, questionType: String, isMultipleResponse: Boolean, questionText: Option[String], answers: Option[Array[AnswerVO]])
case class PollVO(id: String, questions: Array[QuestionVO], title: Option[String], started: Boolean, stopped: Boolean, showResult: Boolean)
case class UserVO(id: String, externalId: String, name: String, role: String,

View File

@ -44,11 +44,11 @@ case class ShowPollResultReqMsgBody(requesterId: String, pollId: String)
object StartCustomPollReqMsg { val NAME = "StartCustomPollReqMsg" }
case class StartCustomPollReqMsg(header: BbbClientMsgHeader, body: StartCustomPollReqMsgBody) extends StandardMsg
case class StartCustomPollReqMsgBody(requesterId: String, pollId: String, pollType: String, isMultipleChoice: Boolean, answers: Seq[String])
case class StartCustomPollReqMsgBody(requesterId: String, pollId: String, pollType: String, isMultipleResponse: Boolean, answers: Seq[String])
object StartPollReqMsg { val NAME = "StartPollReqMsg" }
case class StartPollReqMsg(header: BbbClientMsgHeader, body: StartPollReqMsgBody) extends StandardMsg
case class StartPollReqMsgBody(requesterId: String, pollId: String, pollType: String, isMultipleChoice: Boolean)
case class StartPollReqMsgBody(requesterId: String, pollId: String, pollType: String, isMultipleResponse: Boolean)
object StopPollReqMsg { val NAME = "StopPollReqMsg" }
case class StopPollReqMsg(header: BbbClientMsgHeader, body: StopPollReqMsgBody) extends StandardMsg

View File

@ -2,7 +2,7 @@ import RedisPubSub from '/imports/startup/server/redis';
import { check } from 'meteor/check';
import { extractCredentials } from '/imports/api/common/server/helpers';
export default function startPoll(pollType, pollId, isMultipleChoice, answers) {
export default function startPoll(pollType, pollId, isMultipleResponse, answers) {
const REDIS_CONFIG = Meteor.settings.private.redis;
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
@ -17,7 +17,7 @@ export default function startPoll(pollType, pollId, isMultipleChoice, answers) {
requesterId: requesterUserId,
pollId: `${pollId}/${new Date().getTime()}`,
pollType,
isMultipleChoice,
isMultipleResponse,
};
if (pollType === 'custom') {

View File

@ -15,7 +15,7 @@ export default function addPoll(meetingId, requesterId, poll) {
key: String,
},
],
isMultipleChoice: Boolean
isMultipleResponse: Boolean,
});
const userSelector = {

View File

@ -86,10 +86,10 @@ const intlMessages = defineMessages({
id: 'app.poll.a5',
description: 'label for A / B / C / D / E poll',
},
enableMultipleChoiceLabel : {
id: 'app.poll.enableMultipleChoiceLabel',
enableMultipleResponseLabel: {
id: 'app.poll.enableMultipleResponseLabel',
description: 'label for checkbox to enable multiple choice',
}
},
});
const CHAT_ENABLED = Meteor.settings.public.chat.enabled;
@ -104,13 +104,13 @@ class Poll extends Component {
customPollReq: false,
isPolling: false,
customPollValues: [],
isMultipleChoice: false,
isMultipleResponse: false,
};
this.inputEditor = [];
this.toggleCustomFields = this.toggleCustomFields.bind(this);
this.toggleIsMultipleChoice = this.toggleIsMultipleChoice.bind(this);
this.toggleIsMultipleResponse = this.toggleIsMultipleResponse.bind(this);
this.renderQuickPollBtns = this.renderQuickPollBtns.bind(this);
this.renderCustomView = this.renderCustomView.bind(this);
this.renderInputFields = this.renderInputFields.bind(this);
@ -166,16 +166,16 @@ class Poll extends Component {
return this.setState({ customPollReq: !customPollReq });
}
toggleIsMultipleChoice() {
const { isMultipleChoice } = this.state;
return this.setState({ isMultipleChoice: !isMultipleChoice });
toggleIsMultipleResponse() {
const { isMultipleResponse } = this.state;
return this.setState({ isMultipleResponse: !isMultipleResponse });
}
renderQuickPollBtns() {
const {
isMeteorConnected, pollTypes, startPoll, intl,
} = this.props;
const { isMultipleChoice } = this.state;
const { isMultipleResponse } = this.state;
const btns = pollTypes.map((type) => {
if (type === 'custom') return false;
@ -195,7 +195,7 @@ class Poll extends Component {
key={_.uniqueId('quick-poll-')}
onClick={() => {
Session.set('pollInitiated', true);
this.setState({ isPolling: true }, () => startPoll(type, isMultipleChoice));
this.setState({ isPolling: true }, () => startPoll(type, isMultipleResponse));
}}
/>);
});
@ -205,7 +205,7 @@ class Poll extends Component {
renderCustomView() {
const { intl, startCustomPoll } = this.props;
const { isMultipleChoice } = this.state;
const { isMultipleResponse } = this.state;
const isDisabled = _.compact(this.inputEditor).length < 1;
return (
@ -215,7 +215,7 @@ class Poll extends Component {
onClick={() => {
if (this.inputEditor.length > 0) {
Session.set('pollInitiated', true);
this.setState({ isPolling: true }, () => startCustomPoll('custom', isMultipleChoice, _.compact(this.inputEditor)));
this.setState({ isPolling: true }, () => startCustomPoll('custom', isMultipleResponse, _.compact(this.inputEditor)));
}
}}
label={intl.formatMessage(intlMessages.startCustomLabel)}
@ -285,19 +285,19 @@ class Poll extends Component {
renderPollOptions() {
const { isMeteorConnected, intl } = this.props;
const { customPollReq, isMultipleChoice } = this.state;
const { customPollReq, isMultipleResponse } = this.state;
return (
<div>
<div>
<input
id="multipleChoiceCheckboxId"
type="checkbox"
onChange={this.toggleIsMultipleChoice}
checked={isMultipleChoice}
id="multipleResponseCheckboxId"
type="checkbox"
onChange={this.toggleIsMultipleResponse}
checked={isMultipleResponse}
/>
<label htmlFor="multipleChoiceCheckboxId">
{intl.formatMessage(intlMessages.enableMultipleChoiceLabel)}
<label htmlFor="multipleResponseCheckboxId">
{intl.formatMessage(intlMessages.enableMultipleResponseLabel)}
</label>
</div>
<div className={styles.instructions}>

View File

@ -22,9 +22,9 @@ export default withTracker(() => {
const pollId = currentSlide ? currentSlide.id : PUBLIC_CHAT_KEY;
const startPoll = (type, isMultipleChoice) => makeCall('startPoll', type, pollId, isMultipleChoice);
const startPoll = (type, isMultipleResponse) => makeCall('startPoll', type, pollId, isMultipleResponse);
const startCustomPoll = (type, isMultipleChoice, answers) => makeCall('startPoll', type, pollId, isMultipleChoice, answers);
const startCustomPoll = (type, isMultipleResponse, answers) => makeCall('startPoll', type, pollId, isMultipleResponse, answers);
const stopPoll = () => makeCall('stopPoll');

View File

@ -65,10 +65,10 @@ class LiveResult extends PureComponent {
if (responses) {
const response = responses.find(r => r.userId === user.userId);
if (response) {
let answerKeys = [];
const answerKeys = [];
response.answerIds.forEach((answerId) => {
answerKeys.push(answers[answerId].key);
})
});
answer = answerKeys.join(', ');
}
}
@ -198,14 +198,14 @@ class LiveResult extends PureComponent {
onClick={() => {
Session.set('pollInitiated', false);
Service.publishPoll();
const { answers, numRespondents } = currentPoll;
const { answers, numResponders } = currentPoll;
let responded = 0;
let resultString = 'bbb-published-poll-\n';
answers.map((item) => {
responded += item.numVotes;
return item;
}).map((item) => {
const numResponded = responded === numRespondents ? numRespondents : responded;
}).forEach((item) => {
const numResponded = numResponders || responded;
const pct = Math.round(item.numVotes / numResponded * 100);
const pctFotmatted = `${Number.isNaN(pct) ? 0 : pct}%`;
resultString += `${item.key}: ${item.numVotes || 0} | ${pctFotmatted}\n`;

View File

@ -4,9 +4,9 @@ import Button from '/imports/ui/components/button/component';
import injectWbResizeEvent from '/imports/ui/components/presentation/resize-wrapper/component';
import { defineMessages, injectIntl } from 'react-intl';
import cx from 'classnames';
import { Meteor } from 'meteor/meteor';
import { styles } from './styles.scss';
import AudioService from '/imports/ui/components/audio/service';
import {Meteor} from "meteor/meteor";
const intlMessages = defineMessages({
pollingTitleLabel: {
@ -18,8 +18,8 @@ const intlMessages = defineMessages({
pollAnswerDesc: {
id: 'app.polling.pollAnswerDesc',
},
pollSendLabel: {
id: 'app.polling.pollSendLabel',
pollSubmitLabel: {
id: 'app.polling.pollSubmitLabel',
},
});
@ -28,8 +28,8 @@ class Polling extends Component {
super(props);
this.state = {
checkedAnswers: []
}
checkedAnswers: [],
};
this.play = this.play.bind(this);
this.renderButtonAnswers = this.renderButtonAnswers.bind(this);
@ -66,52 +66,52 @@ class Polling extends Component {
}
return (
<div
key={pollAnswer.id}
className={styles.pollButtonWrapper}
>
<Button
disabled={!isMeteorConnected}
className={styles.pollingButton}
color="primary"
size="md"
label={label}
key={pollAnswer.key}
onClick={() => handleVote(poll.pollId, [pollAnswer.id])}
aria-labelledby={`pollAnswerLabel${pollAnswer.key}`}
aria-describedby={`pollAnswerDesc${pollAnswer.key}`}
/>
<div
key={pollAnswer.id}
className={styles.pollButtonWrapper}
className={styles.hidden}
id={`pollAnswerLabel${pollAnswer.key}`}
>
<Button
disabled={!isMeteorConnected}
className={styles.pollingButton}
color="primary"
size="md"
label={label}
key={pollAnswer.key}
onClick={() => handleVote(poll.pollId, [pollAnswer.id])}
aria-labelledby={`pollAnswerLabel${pollAnswer.key}`}
aria-describedby={`pollAnswerDesc${pollAnswer.key}`}
/>
<div
className={styles.hidden}
id={`pollAnswerLabel${pollAnswer.key}`}
>
{intl.formatMessage(intlMessages.pollAnswerLabel, { 0: label })}
</div>
<div
className={styles.hidden}
id={`pollAnswerDesc${pollAnswer.key}`}
>
{intl.formatMessage(intlMessages.pollAnswerDesc, { 0: label })}
</div>
{intl.formatMessage(intlMessages.pollAnswerLabel, { 0: label })}
</div>
<div
className={styles.hidden}
id={`pollAnswerDesc${pollAnswer.key}`}
>
{intl.formatMessage(intlMessages.pollAnswerDesc, { 0: label })}
</div>
</div>
);
})
});
}
handleCheckboxChange(pollId, answerId) {
const {checkedAnswers} = this.state
const { checkedAnswers } = this.state;
if (checkedAnswers.includes(answerId)) {
checkedAnswers.splice(checkedAnswers.indexOf(answerId), 1)
checkedAnswers.splice(checkedAnswers.indexOf(answerId), 1);
} else {
checkedAnswers.push(answerId)
checkedAnswers.push(answerId);
}
checkedAnswers.sort();
}
handleSubmit(pollId) {
const {handleVote} = this.props;
const {checkedAnswers} = this.state
handleVote(pollId, checkedAnswers)
const { handleVote } = this.props;
const { checkedAnswers } = this.state;
handleVote(pollId, checkedAnswers);
}
renderCheckboxAnswers() {
@ -132,48 +132,48 @@ class Polling extends Component {
}
return (
<div
key={pollAnswer.id}
>
<input
type="checkbox"
disabled={!isMeteorConnected}
id={`answerInput${pollAnswer.key}`}
onChange={() => this.handleCheckboxChange(poll.pollId, pollAnswer.id)}
aria-labelledby={`pollAnswerLabel${pollAnswer.key}`}
aria-describedby={`pollAnswerDesc${pollAnswer.key}`}
/>
<label htmlFor={`answerInput${pollAnswer.key}`}>
{label}
</label>
<div
key={pollAnswer.id}
className={styles.hidden}
id={`pollAnswerLabel${pollAnswer.key}`}
>
<input
type="checkbox"
disabled={!isMeteorConnected}
id={`answerInput${pollAnswer.key}`}
onChange={() => this.handleCheckboxChange(poll.pollId, pollAnswer.id)}
aria-labelledby={`pollAnswerLabel${pollAnswer.key}`}
aria-describedby={`pollAnswerDesc${pollAnswer.key}`}
/>
<label htmlFor={`answerInput${pollAnswer.key}`}>
{label}
</label>
<div
className={styles.hidden}
id={`pollAnswerLabel${pollAnswer.key}`}
>
{intl.formatMessage(intlMessages.pollAnswerLabel, { 0: label })}
</div>
<div
className={styles.hidden}
id={`pollAnswerDesc${pollAnswer.key}`}
>
{intl.formatMessage(intlMessages.pollAnswerDesc, { 0: label })}
</div>
{intl.formatMessage(intlMessages.pollAnswerLabel, { 0: label })}
</div>
<div
className={styles.hidden}
id={`pollAnswerDesc${pollAnswer.key}`}
>
{intl.formatMessage(intlMessages.pollAnswerDesc, { 0: label })}
</div>
</div>
);
})}
</div>
<div>
<Button
disabled={!isMeteorConnected}
className={styles.pollingButton}
color="primary"
size="md"
label={intl.formatMessage(intlMessages.pollSendLabel)}
onClick={() => this.handleSubmit(poll.pollId)}
disabled={!isMeteorConnected}
className={styles.pollingButton}
color="primary"
size="md"
label={intl.formatMessage(intlMessages.pollSubmitLabel)}
onClick={() => this.handleSubmit(poll.pollId)}
/>
</div>
</div>
)
);
}
render() {
@ -207,7 +207,7 @@ class Polling extends Component {
{intl.formatMessage(intlMessages.pollingTitleLabel)}
</div>
<div className={cx(pollAnswerStyles)}>
{poll.isMultipleChoice ? this.renderCheckboxAnswers() : this.renderButtonAnswers()}
{poll.isMultipleResponse ? this.renderCheckboxAnswers() : this.renderButtonAnswers()}
</div>
</div>
</div>);

View File

@ -31,7 +31,7 @@ const mapPolls = () => {
poll: {
answers: poll.answers,
pollId: poll.id,
isMultipleChoice: poll.isMultipleChoice,
isMultipleResponse: poll.isMultipleResponse,
stackOptions,
},
pollExists: true,

View File

@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
import PollService from '/imports/ui/components/poll/service';
import { injectIntl, defineMessages } from 'react-intl';
import styles from './styles';
import { prototype } from 'clipboard';
const intlMessages = defineMessages({
pollResultAria: {
@ -66,7 +65,7 @@ class PollDrawComponent extends Component {
// rendering / rerendering the text objects
const { annotation } = this.props;
const { points, result } = annotation;
const { points, result, numResponders } = annotation;
const { slideWidth, slideHeight, intl } = this.props;
// x1 and y1 - coordinates of the top left corner of the annotation
@ -84,18 +83,14 @@ class PollDrawComponent extends Component {
const width = ((initialWidth - 0.001) / 100) * slideWidth;
const height = ((initialHeight - 0.001) / 100) * slideHeight;
let votesTotal = 0;
let maxNumVotes = 0;
const textArray = [];
// counting the total number of votes, finding the biggest number of votes
result.reduce((previousValue, currentValue) => {
votesTotal = previousValue + currentValue.numVotes;
if (maxNumVotes < currentValue.numVotes) {
maxNumVotes = currentValue.numVotes;
const maxNumVotes = result.reduce((previousValue, currentValue) => {
if (previousValue < currentValue.numVotes) {
return currentValue.numVotes;
}
return votesTotal;
return previousValue;
}, 0);
// filling the textArray with data to display
@ -129,11 +124,11 @@ class PollDrawComponent extends Component {
}
_tempArray.push(_result.key, `${_result.numVotes}`);
if (votesTotal === 0) {
if (numResponders === 0) {
_tempArray.push('0%');
_tempArray.push(i);
} else {
const percResult = (_result.numVotes / votesTotal) * 100;
const percResult = (_result.numVotes / numResponders) * 100;
_tempArray.push(`${Math.round(percResult)}%`);
_tempArray.push(i);
}
@ -604,7 +599,7 @@ class PollDrawComponent extends Component {
const { prepareToDisplay, textArray } = this.state;
let ariaResultLabel = `${intl.formatMessage(intlMessages.pollResultAria)}: `;
textArray.map((t, idx) => {
textArray.forEach((t, idx) => {
const pollLine = t.slice(0, -1);
ariaResultLabel += `${idx > 0 ? ' |' : ''} ${pollLine.join(' | ')}`;
});

View File

@ -213,7 +213,7 @@
"app.presentationUploder.clearErrors": "Fehler löschen",
"app.presentationUploder.clearErrorsDesc": "Löscht fehlgeschlagene Präsentationsuploads",
"app.poll.pollPaneTitle": "Umfrage",
"app.poll.enableMultipleChoiceLabel": "Multiple-Choice?",
"app.poll.enableMultipleResponseLabel": "Mehrere Antworten pro Teilnehmer zulassen?",
"app.poll.quickPollTitle": "Schnellumfrage",
"app.poll.hidePollDesc": "Versteckt das Umfragemenü",
"app.poll.customPollInstruction": "Um eine benutzerdefinierte Umfrage zu erstellen, wählen Sie die Schaltfläche unten und geben Sie Ihre Optionen ein.",
@ -253,6 +253,7 @@
"app.polling.pollingTitle": "Umfrageoptionen",
"app.polling.pollAnswerLabel": "Umfrageantwort {0}",
"app.polling.pollAnswerDesc": "Diese Option auswählen für Umfrage {0}",
"app.polling.pollSubmitLabel": "Abgeben",
"app.failedMessage": "Es gibt Verbindungsprobleme mit dem Server.",
"app.downloadPresentationButton.label": "Ursprüngliche Version der Präsentation herunterladen",
"app.connectingMessage": "Verbinde...",

View File

@ -213,7 +213,7 @@
"app.presentationUploder.clearErrors": "Clear errors",
"app.presentationUploder.clearErrorsDesc": "Clears failed presentation uploads",
"app.poll.pollPaneTitle": "Polling",
"app.poll.enableMultipleChoiceLabel": "Multiple response?",
"app.poll.enableMultipleResponseLabel": "Allow multiple answers per respondent?",
"app.poll.quickPollTitle": "Quick Poll",
"app.poll.hidePollDesc": "Hides the poll menu pane",
"app.poll.customPollInstruction": "To create a custom poll, select the button below and input your options.",
@ -256,6 +256,7 @@
"app.polling.pollingTitle": "Polling options",
"app.polling.pollAnswerLabel": "Poll answer {0}",
"app.polling.pollAnswerDesc": "Select this option to vote for {0}",
"app.polling.pollSubmitLabel": "Submit",
"app.failedMessage": "Apologies, trouble connecting to the server.",
"app.downloadPresentationButton.label": "Download the original presentation",
"app.connectingMessage": "Connecting ...",