You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

161 lines
5.9 KiB

  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import ImmutablePropTypes from 'react-immutable-proptypes';
  4. import ImmutablePureComponent from 'react-immutable-pure-component';
  5. import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
  6. import IconButton from 'mastodon/components/icon_button';
  7. import Icon from 'mastodon/components/icon';
  8. import AutosuggestInput from 'mastodon/components/autosuggest_input';
  9. import classNames from 'classnames';
  10. const messages = defineMessages({
  11. option_placeholder: { id: 'compose_form.poll.option_placeholder', defaultMessage: 'Choice {number}' },
  12. add_option: { id: 'compose_form.poll.add_option', defaultMessage: 'Add a choice' },
  13. remove_option: { id: 'compose_form.poll.remove_option', defaultMessage: 'Remove this choice' },
  14. poll_duration: { id: 'compose_form.poll.duration', defaultMessage: 'Poll duration' },
  15. minutes: { id: 'intervals.full.minutes', defaultMessage: '{number, plural, one {# minute} other {# minutes}}' },
  16. hours: { id: 'intervals.full.hours', defaultMessage: '{number, plural, one {# hour} other {# hours}}' },
  17. days: { id: 'intervals.full.days', defaultMessage: '{number, plural, one {# day} other {# days}}' },
  18. });
  19. @injectIntl
  20. class Option extends React.PureComponent {
  21. static propTypes = {
  22. title: PropTypes.string.isRequired,
  23. index: PropTypes.number.isRequired,
  24. isPollMultiple: PropTypes.bool,
  25. onChange: PropTypes.func.isRequired,
  26. onRemove: PropTypes.func.isRequired,
  27. onToggleMultiple: PropTypes.func.isRequired,
  28. suggestions: ImmutablePropTypes.list,
  29. onClearSuggestions: PropTypes.func.isRequired,
  30. onFetchSuggestions: PropTypes.func.isRequired,
  31. onSuggestionSelected: PropTypes.func.isRequired,
  32. intl: PropTypes.object.isRequired,
  33. };
  34. handleOptionTitleChange = e => {
  35. this.props.onChange(this.props.index, e.target.value);
  36. };
  37. handleOptionRemove = () => {
  38. this.props.onRemove(this.props.index);
  39. };
  40. handleToggleMultiple = e => {
  41. this.props.onToggleMultiple();
  42. e.preventDefault();
  43. e.stopPropagation();
  44. };
  45. onSuggestionsClearRequested = () => {
  46. this.props.onClearSuggestions();
  47. }
  48. onSuggestionsFetchRequested = (token) => {
  49. this.props.onFetchSuggestions(token);
  50. }
  51. onSuggestionSelected = (tokenStart, token, value) => {
  52. this.props.onSuggestionSelected(tokenStart, token, value, ['poll', 'options', this.props.index]);
  53. }
  54. render () {
  55. const { isPollMultiple, title, index, intl } = this.props;
  56. return (
  57. <li>
  58. <label className='poll__text editable'>
  59. <span
  60. className={classNames('poll__input', { checkbox: isPollMultiple })}
  61. onClick={this.handleToggleMultiple}
  62. role='button'
  63. tabIndex='0'
  64. />
  65. <AutosuggestInput
  66. placeholder={intl.formatMessage(messages.option_placeholder, { number: index + 1 })}
  67. maxLength={25}
  68. value={title}
  69. onChange={this.handleOptionTitleChange}
  70. suggestions={this.props.suggestions}
  71. onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
  72. onSuggestionsClearRequested={this.onSuggestionsClearRequested}
  73. onSuggestionSelected={this.onSuggestionSelected}
  74. searchTokens={[':']}
  75. />
  76. </label>
  77. <div className='poll__cancel'>
  78. <IconButton disabled={index <= 1} title={intl.formatMessage(messages.remove_option)} icon='times' onClick={this.handleOptionRemove} />
  79. </div>
  80. </li>
  81. );
  82. }
  83. }
  84. export default
  85. @injectIntl
  86. class PollForm extends ImmutablePureComponent {
  87. static propTypes = {
  88. options: ImmutablePropTypes.list,
  89. expiresIn: PropTypes.number,
  90. isMultiple: PropTypes.bool,
  91. onChangeOption: PropTypes.func.isRequired,
  92. onAddOption: PropTypes.func.isRequired,
  93. onRemoveOption: PropTypes.func.isRequired,
  94. onChangeSettings: PropTypes.func.isRequired,
  95. suggestions: ImmutablePropTypes.list,
  96. onClearSuggestions: PropTypes.func.isRequired,
  97. onFetchSuggestions: PropTypes.func.isRequired,
  98. onSuggestionSelected: PropTypes.func.isRequired,
  99. intl: PropTypes.object.isRequired,
  100. };
  101. handleAddOption = () => {
  102. this.props.onAddOption('');
  103. };
  104. handleSelectDuration = e => {
  105. this.props.onChangeSettings(e.target.value, this.props.isMultiple);
  106. };
  107. handleToggleMultiple = () => {
  108. this.props.onChangeSettings(this.props.expiresIn, !this.props.isMultiple);
  109. };
  110. render () {
  111. const { options, expiresIn, isMultiple, onChangeOption, onRemoveOption, intl, ...other } = this.props;
  112. if (!options) {
  113. return null;
  114. }
  115. return (
  116. <div className='compose-form__poll-wrapper'>
  117. <ul>
  118. {options.map((title, i) => <Option title={title} key={i} index={i} onChange={onChangeOption} onRemove={onRemoveOption} isPollMultiple={isMultiple} onToggleMultiple={this.handleToggleMultiple} {...other} />)}
  119. </ul>
  120. <div className='poll__footer'>
  121. <button disabled={options.size >= 4} className='button button-secondary' onClick={this.handleAddOption}><Icon id='plus' /> <FormattedMessage {...messages.add_option} /></button>
  122. <select value={expiresIn} onBlur={this.handleSelectDuration}>
  123. <option value={300}>{intl.formatMessage(messages.minutes, { number: 5 })}</option>
  124. <option value={1800}>{intl.formatMessage(messages.minutes, { number: 30 })}</option>
  125. <option value={3600}>{intl.formatMessage(messages.hours, { number: 1 })}</option>
  126. <option value={21600}>{intl.formatMessage(messages.hours, { number: 6 })}</option>
  127. <option value={86400}>{intl.formatMessage(messages.days, { number: 1 })}</option>
  128. <option value={259200}>{intl.formatMessage(messages.days, { number: 3 })}</option>
  129. <option value={604800}>{intl.formatMessage(messages.days, { number: 7 })}</option>
  130. </select>
  131. </div>
  132. </div>
  133. );
  134. }
  135. }