2018-09-15 01:50:18 +08:00
|
|
|
import React, { Component } from 'react';
|
|
|
|
import { Link } from 'react-router';
|
|
|
|
import Button from '/imports/ui/components/button/component';
|
|
|
|
import Icon from '/imports/ui/components/icon/component';
|
2018-09-24 06:20:20 +08:00
|
|
|
import { findDOMNode } from 'react-dom';
|
2018-09-24 06:57:03 +08:00
|
|
|
import { defineMessages, injectIntl } from 'react-intl';
|
|
|
|
import _ from 'lodash';
|
2018-09-15 01:50:18 +08:00
|
|
|
import { styles } from './styles.scss';
|
|
|
|
|
2018-09-24 06:57:03 +08:00
|
|
|
const intlMessages = defineMessages({
|
|
|
|
pollPaneTitle: {
|
|
|
|
id: 'app.poll.pollPaneTitle',
|
|
|
|
description: 'heading label for the poll menu',
|
|
|
|
},
|
|
|
|
hidePollDesc: {
|
|
|
|
id: 'app.poll.hidePollDesc',
|
|
|
|
description: 'aria label description for hide poll button',
|
|
|
|
},
|
|
|
|
customPollLabel: {
|
|
|
|
id: 'app.poll.customPollLabel',
|
|
|
|
description: 'label for custom poll button',
|
|
|
|
},
|
|
|
|
startCustomLabel: {
|
|
|
|
id: 'app.poll.startCustomLabel',
|
|
|
|
description: 'label for button to start custom poll',
|
|
|
|
},
|
|
|
|
customPollInstruction: {
|
|
|
|
id: 'app.poll.customPollInstruction',
|
|
|
|
description: 'instructions for using custom poll',
|
|
|
|
},
|
|
|
|
quickPollInstruction: {
|
|
|
|
id: 'app.poll.quickPollInstruction',
|
|
|
|
description: 'instructions for using pre configured polls',
|
|
|
|
},
|
2018-09-25 06:43:54 +08:00
|
|
|
activePollInstruction: {
|
|
|
|
id: 'app.poll.activePollInstruction',
|
|
|
|
description: 'instructions displayed when a poll is active',
|
|
|
|
},
|
|
|
|
publishLabel: {
|
|
|
|
id: 'app.poll.publishLabel',
|
|
|
|
description: 'label for the publish button',
|
|
|
|
},
|
|
|
|
backLabel: {
|
|
|
|
id: 'app.poll.backLabel',
|
|
|
|
description: 'label for the return to poll options button',
|
|
|
|
},
|
2018-09-24 06:57:03 +08:00
|
|
|
customPlaceholder: {
|
|
|
|
id: 'app.poll.customPlaceholder',
|
|
|
|
description: 'custom poll input field placeholder text',
|
|
|
|
},
|
|
|
|
truefalse: {
|
|
|
|
id: 'app.poll.truefalse',
|
|
|
|
description: 'label for true / false poll',
|
|
|
|
},
|
|
|
|
yesno: {
|
|
|
|
id: 'app.poll.yesno',
|
|
|
|
description: 'label for Yes / No poll',
|
|
|
|
},
|
|
|
|
ab: {
|
|
|
|
id: 'app.poll.ab',
|
|
|
|
description: 'label for A / B poll',
|
|
|
|
},
|
|
|
|
abc: {
|
|
|
|
id: 'app.poll.abc',
|
|
|
|
description: 'label for A / B / C poll',
|
|
|
|
},
|
|
|
|
abcd: {
|
|
|
|
id: 'app.poll.abcd',
|
|
|
|
description: 'label for A / B / C / D poll',
|
|
|
|
},
|
|
|
|
abcde: {
|
|
|
|
id: 'app.poll.abcde',
|
|
|
|
description: 'label for A / B / C / D / E poll',
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2018-10-03 07:59:45 +08:00
|
|
|
const MAX_CUSTOM_FIELDS = Meteor.settings.public.poll.max_custom;
|
|
|
|
|
2018-09-15 01:50:18 +08:00
|
|
|
class Poll extends Component {
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
this.state = {
|
2018-09-24 06:57:03 +08:00
|
|
|
customPollReq: false,
|
2018-09-25 06:43:54 +08:00
|
|
|
isPolling: false,
|
2018-09-15 01:50:18 +08:00
|
|
|
};
|
|
|
|
|
2018-09-24 06:57:03 +08:00
|
|
|
this.pollOptions = [];
|
|
|
|
|
2018-09-15 01:50:18 +08:00
|
|
|
this.toggleCustomFields = this.toggleCustomFields.bind(this);
|
2018-09-24 06:57:03 +08:00
|
|
|
this.renderQuickPollBtns = this.renderQuickPollBtns.bind(this);
|
2018-09-24 06:20:20 +08:00
|
|
|
this.renderInputFields = this.renderInputFields.bind(this);
|
|
|
|
this.validateInputField = this.validateInputField.bind(this);
|
2018-09-26 00:54:07 +08:00
|
|
|
this.nonPresenterRedirect = this.nonPresenterRedirect.bind(this);
|
2018-09-24 06:20:20 +08:00
|
|
|
this.getInputFields = this.getInputFields.bind(this);
|
2018-09-24 06:57:03 +08:00
|
|
|
}
|
|
|
|
|
2018-09-26 00:54:07 +08:00
|
|
|
componentWillMount() {
|
|
|
|
this.nonPresenterRedirect();
|
|
|
|
}
|
|
|
|
|
2018-09-24 06:20:20 +08:00
|
|
|
componentDidUpdate(prevProps, prevState) {
|
2018-09-26 00:54:07 +08:00
|
|
|
this.nonPresenterRedirect();
|
|
|
|
}
|
2018-09-24 06:20:20 +08:00
|
|
|
|
2018-09-26 00:54:07 +08:00
|
|
|
nonPresenterRedirect() {
|
|
|
|
const { currentUser, router } = this.props;
|
|
|
|
if (!currentUser.presenter) return router.push('/users');
|
2018-09-24 06:57:03 +08:00
|
|
|
}
|
|
|
|
|
2018-09-24 06:20:20 +08:00
|
|
|
getInputFields() {
|
2018-09-24 06:57:03 +08:00
|
|
|
const { intl } = this.props;
|
2018-09-24 06:20:20 +08:00
|
|
|
const items = [];
|
|
|
|
|
2018-10-03 07:59:45 +08:00
|
|
|
for (let i = 0; i < MAX_CUSTOM_FIELDS; i++) {
|
2018-09-24 06:20:20 +08:00
|
|
|
items.push(<input
|
2018-09-24 06:57:03 +08:00
|
|
|
key={_.uniqueId('custom-poll-')}
|
|
|
|
placeholder={intl.formatMessage(intlMessages.customPlaceholder)}
|
2018-09-24 06:20:20 +08:00
|
|
|
className={styles.input}
|
|
|
|
ref={(node) => { this[`pollInput${i}`] = node; }}
|
|
|
|
onChange={() => this.validateInputField(this[`pollInput${i}`])}
|
|
|
|
data-index={i}
|
|
|
|
/>);
|
|
|
|
}
|
|
|
|
|
|
|
|
return items;
|
2018-09-24 06:57:03 +08:00
|
|
|
}
|
|
|
|
|
2018-09-24 06:20:20 +08:00
|
|
|
validateInputField(ref) {
|
2018-10-03 04:45:41 +08:00
|
|
|
// This regex will replace any instance of 2 or more consecutive white spaces
|
|
|
|
// with a single white space character.
|
2018-09-24 06:20:20 +08:00
|
|
|
const option = ref.value.replace(/\s{2,}/g, ' ').trim();
|
|
|
|
const index = ref.getAttribute('data-index');
|
2018-09-24 06:57:03 +08:00
|
|
|
|
2018-09-24 06:20:20 +08:00
|
|
|
this.pollOptions[index] = option === '' ? '' : option;
|
2018-09-24 06:57:03 +08:00
|
|
|
|
2018-09-24 06:20:20 +08:00
|
|
|
return _.compact(this.pollOptions).length > 1
|
|
|
|
? findDOMNode(this.startPollBtn).setAttribute('aria-disabled', 'false')
|
|
|
|
: findDOMNode(this.startPollBtn).setAttribute('aria-disabled', 'true');
|
2018-09-15 01:50:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
toggleCustomFields() {
|
2018-09-24 06:57:03 +08:00
|
|
|
const { customPollReq } = this.state;
|
2018-09-24 06:20:20 +08:00
|
|
|
|
|
|
|
this.pollOptions = [];
|
|
|
|
|
|
|
|
return customPollReq
|
|
|
|
? this.setState({ customPollReq: false })
|
|
|
|
: this.setState({ customPollReq: true });
|
2018-09-24 06:57:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
renderQuickPollBtns() {
|
|
|
|
const { pollTypes, startPoll, intl } = this.props;
|
|
|
|
|
2018-09-24 06:20:20 +08:00
|
|
|
const btns = pollTypes.reduce((arr, type) => {
|
|
|
|
if (type === 'custom') return arr;
|
|
|
|
|
2018-09-24 06:57:03 +08:00
|
|
|
let label = '';
|
|
|
|
|
2018-09-24 06:20:20 +08:00
|
|
|
if (type === 'YN') label = intl.formatMessage(intlMessages.yesno);
|
|
|
|
if (type === 'TF') label = intl.formatMessage(intlMessages.truefalse);
|
|
|
|
if (type === 'A-2') label = intl.formatMessage(intlMessages.ab);
|
|
|
|
if (type === 'A-3') label = intl.formatMessage(intlMessages.abc);
|
|
|
|
if (type === 'A-4') label = intl.formatMessage(intlMessages.abcd);
|
|
|
|
if (type === 'A-5') label = intl.formatMessage(intlMessages.abcde);
|
|
|
|
|
|
|
|
arr.push(<Button
|
2018-09-24 06:57:03 +08:00
|
|
|
label={label}
|
2018-10-03 07:08:32 +08:00
|
|
|
color="default"
|
2018-09-24 06:57:03 +08:00
|
|
|
className={styles.pollBtn}
|
|
|
|
key={_.uniqueId('quick-poll-')}
|
2018-09-25 06:43:54 +08:00
|
|
|
onClick={() => {
|
|
|
|
this.setState({ isPolling: true }, () => startPoll(type));
|
|
|
|
}}
|
2018-09-24 06:57:03 +08:00
|
|
|
/>);
|
2018-09-24 06:20:20 +08:00
|
|
|
|
|
|
|
return arr;
|
|
|
|
}, []);
|
2018-09-24 06:57:03 +08:00
|
|
|
|
|
|
|
return btns;
|
2018-09-15 01:50:18 +08:00
|
|
|
}
|
|
|
|
|
2018-09-24 06:20:20 +08:00
|
|
|
renderInputFields() {
|
|
|
|
const { intl, startCustomPoll } = this.props;
|
2018-09-24 06:57:03 +08:00
|
|
|
|
|
|
|
return (
|
2018-09-24 06:20:20 +08:00
|
|
|
<div className={styles.customInputWrapper}>
|
|
|
|
{this.getInputFields()}
|
2018-09-24 06:57:03 +08:00
|
|
|
<Button
|
2018-09-24 06:20:20 +08:00
|
|
|
onClick={() => {
|
|
|
|
if (_.compact(this.pollOptions).length > 1) {
|
2018-09-25 06:43:54 +08:00
|
|
|
this.setState({ isPolling: true }, () => startCustomPoll('custom', _.compact(this.pollOptions)));
|
2018-09-24 06:20:20 +08:00
|
|
|
}
|
|
|
|
}}
|
2018-09-24 06:57:03 +08:00
|
|
|
label={intl.formatMessage(intlMessages.startCustomLabel)}
|
2018-09-24 06:20:20 +08:00
|
|
|
color="primary"
|
|
|
|
ref={node => this.startPollBtn = node}
|
|
|
|
aria-disabled
|
|
|
|
className={styles.btn}
|
2018-09-24 06:57:03 +08:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
);
|
2018-09-15 01:50:18 +08:00
|
|
|
}
|
|
|
|
|
2018-09-25 06:43:54 +08:00
|
|
|
renderActivePollOptions() {
|
|
|
|
const {
|
|
|
|
intl, router, publishPoll, stopPoll,
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<div className={styles.instructions}>{intl.formatMessage(intlMessages.activePollInstruction)}</div>
|
|
|
|
<Button
|
|
|
|
onClick={() => {
|
|
|
|
publishPoll();
|
2018-09-28 22:40:53 +08:00
|
|
|
router.push('/users');
|
2018-09-25 06:43:54 +08:00
|
|
|
}}
|
|
|
|
label={intl.formatMessage(intlMessages.publishLabel)}
|
|
|
|
color="primary"
|
|
|
|
className={styles.btn}
|
|
|
|
/>
|
|
|
|
<Button
|
|
|
|
onClick={() => {
|
|
|
|
stopPoll();
|
|
|
|
this.setState({ isPolling: false });
|
|
|
|
}}
|
|
|
|
label={intl.formatMessage(intlMessages.backLabel)}
|
|
|
|
color="default"
|
|
|
|
className={styles.btn}
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderPollOptions() {
|
2018-09-24 06:57:03 +08:00
|
|
|
const { intl } = this.props;
|
|
|
|
const { customPollReq } = this.state;
|
|
|
|
|
2018-09-15 01:50:18 +08:00
|
|
|
return (
|
2018-09-24 06:20:20 +08:00
|
|
|
<div>
|
2018-09-24 06:57:03 +08:00
|
|
|
<div className={styles.instructions}>
|
|
|
|
{intl.formatMessage(intlMessages.quickPollInstruction)}
|
2018-09-15 01:50:18 +08:00
|
|
|
</div>
|
2018-09-24 06:57:03 +08:00
|
|
|
<div className={styles.grid}>
|
|
|
|
{this.renderQuickPollBtns()}
|
|
|
|
</div>
|
|
|
|
<div className={styles.instructions}>
|
|
|
|
{intl.formatMessage(intlMessages.customPollInstruction)}
|
|
|
|
</div>
|
|
|
|
<Button
|
|
|
|
className={styles.customBtn}
|
2018-10-03 07:08:32 +08:00
|
|
|
color="default"
|
2018-09-24 06:57:03 +08:00
|
|
|
onClick={this.toggleCustomFields}
|
|
|
|
label={intl.formatMessage(intlMessages.customPollLabel)}
|
|
|
|
/>
|
2018-09-24 06:20:20 +08:00
|
|
|
{!customPollReq ? null : this.renderInputFields()}
|
2018-09-15 01:50:18 +08:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
2018-09-25 06:43:54 +08:00
|
|
|
|
|
|
|
render() {
|
|
|
|
const {
|
|
|
|
intl, stopPoll,
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<header className={styles.header}>
|
|
|
|
<Link
|
|
|
|
to="/users"
|
|
|
|
role="button"
|
|
|
|
aria-label={intl.formatMessage(intlMessages.hidePollDesc)}
|
|
|
|
onClick={() => {
|
|
|
|
if (this.state.isPolling) {
|
|
|
|
stopPoll();
|
|
|
|
this.setState({ isPolling: false });
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Icon iconName="left_arrow" />{intl.formatMessage(intlMessages.pollPaneTitle)}
|
|
|
|
</Link>
|
|
|
|
</header>
|
|
|
|
{
|
|
|
|
this.state.isPolling ? this.renderActivePollOptions() : this.renderPollOptions()
|
|
|
|
}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
2018-09-15 01:50:18 +08:00
|
|
|
}
|
|
|
|
|
2018-09-24 06:57:03 +08:00
|
|
|
export default injectIntl(Poll);
|