Conflicts: - `app/javascript/mastodon/features/compose/components/poll_form.jsx`: Upstream changed how icons are handled, including on a line modified by glitch-soc to bump the number of poll options. Applied upstream's change, while keeping the increased number of poll options.
		
			
				
	
	
		
			190 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			190 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
import PropTypes from 'prop-types';
 | 
						|
import { PureComponent } from 'react';
 | 
						|
 | 
						|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 | 
						|
 | 
						|
import classNames from 'classnames';
 | 
						|
 | 
						|
import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
						|
import ImmutablePureComponent from 'react-immutable-pure-component';
 | 
						|
 | 
						|
import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg';
 | 
						|
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
 | 
						|
 | 
						|
import AutosuggestInput from 'mastodon/components/autosuggest_input';
 | 
						|
import { Icon }  from 'mastodon/components/icon';
 | 
						|
import { IconButton } from 'mastodon/components/icon_button';
 | 
						|
 | 
						|
const messages = defineMessages({
 | 
						|
  option_placeholder: { id: 'compose_form.poll.option_placeholder', defaultMessage: 'Choice {number}' },
 | 
						|
  add_option: { id: 'compose_form.poll.add_option', defaultMessage: 'Add a choice' },
 | 
						|
  remove_option: { id: 'compose_form.poll.remove_option', defaultMessage: 'Remove this choice' },
 | 
						|
  poll_duration: { id: 'compose_form.poll.duration', defaultMessage: 'Poll duration' },
 | 
						|
  switchToMultiple: { id: 'compose_form.poll.switch_to_multiple', defaultMessage: 'Change poll to allow multiple choices' },
 | 
						|
  switchToSingle: { id: 'compose_form.poll.switch_to_single', defaultMessage: 'Change poll to allow for a single choice' },
 | 
						|
  minutes: { id: 'intervals.full.minutes', defaultMessage: '{number, plural, one {# minute} other {# minutes}}' },
 | 
						|
  hours: { id: 'intervals.full.hours', defaultMessage: '{number, plural, one {# hour} other {# hours}}' },
 | 
						|
  days: { id: 'intervals.full.days', defaultMessage: '{number, plural, one {# day} other {# days}}' },
 | 
						|
});
 | 
						|
 | 
						|
class OptionIntl extends PureComponent {
 | 
						|
 | 
						|
  static propTypes = {
 | 
						|
    title: PropTypes.string.isRequired,
 | 
						|
    lang: PropTypes.string,
 | 
						|
    index: PropTypes.number.isRequired,
 | 
						|
    isPollMultiple: PropTypes.bool,
 | 
						|
    autoFocus: PropTypes.bool,
 | 
						|
    onChange: PropTypes.func.isRequired,
 | 
						|
    onRemove: PropTypes.func.isRequired,
 | 
						|
    onToggleMultiple: PropTypes.func.isRequired,
 | 
						|
    suggestions: ImmutablePropTypes.list,
 | 
						|
    onClearSuggestions: PropTypes.func.isRequired,
 | 
						|
    onFetchSuggestions: PropTypes.func.isRequired,
 | 
						|
    onSuggestionSelected: PropTypes.func.isRequired,
 | 
						|
    intl: PropTypes.object.isRequired,
 | 
						|
  };
 | 
						|
 | 
						|
  handleOptionTitleChange = e => {
 | 
						|
    this.props.onChange(this.props.index, e.target.value);
 | 
						|
  };
 | 
						|
 | 
						|
  handleOptionRemove = () => {
 | 
						|
    this.props.onRemove(this.props.index);
 | 
						|
  };
 | 
						|
 | 
						|
 | 
						|
  handleToggleMultiple = e => {
 | 
						|
    this.props.onToggleMultiple();
 | 
						|
    e.preventDefault();
 | 
						|
    e.stopPropagation();
 | 
						|
  };
 | 
						|
 | 
						|
  handleCheckboxKeypress = e => {
 | 
						|
    if (e.key === 'Enter' || e.key === ' ') {
 | 
						|
      this.handleToggleMultiple(e);
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
  onSuggestionsClearRequested = () => {
 | 
						|
    this.props.onClearSuggestions();
 | 
						|
  };
 | 
						|
 | 
						|
  onSuggestionsFetchRequested = (token) => {
 | 
						|
    this.props.onFetchSuggestions(token);
 | 
						|
  };
 | 
						|
 | 
						|
  onSuggestionSelected = (tokenStart, token, value) => {
 | 
						|
    this.props.onSuggestionSelected(tokenStart, token, value, ['poll', 'options', this.props.index]);
 | 
						|
  };
 | 
						|
 | 
						|
  render () {
 | 
						|
    const { isPollMultiple, title, lang, index, autoFocus, intl } = this.props;
 | 
						|
 | 
						|
    return (
 | 
						|
      <li>
 | 
						|
        <label className='poll__option editable'>
 | 
						|
          <span
 | 
						|
            className={classNames('poll__input', { checkbox: isPollMultiple })}
 | 
						|
            onClick={this.handleToggleMultiple}
 | 
						|
            onKeyPress={this.handleCheckboxKeypress}
 | 
						|
            role='button'
 | 
						|
            tabIndex={0}
 | 
						|
            title={intl.formatMessage(isPollMultiple ? messages.switchToSingle : messages.switchToMultiple)}
 | 
						|
            aria-label={intl.formatMessage(isPollMultiple ? messages.switchToSingle : messages.switchToMultiple)}
 | 
						|
          />
 | 
						|
 | 
						|
          <AutosuggestInput
 | 
						|
            placeholder={intl.formatMessage(messages.option_placeholder, { number: index + 1 })}
 | 
						|
            maxLength={100}
 | 
						|
            value={title}
 | 
						|
            lang={lang}
 | 
						|
            spellCheck
 | 
						|
            onChange={this.handleOptionTitleChange}
 | 
						|
            suggestions={this.props.suggestions}
 | 
						|
            onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
 | 
						|
            onSuggestionsClearRequested={this.onSuggestionsClearRequested}
 | 
						|
            onSuggestionSelected={this.onSuggestionSelected}
 | 
						|
            searchTokens={[':']}
 | 
						|
            autoFocus={autoFocus}
 | 
						|
          />
 | 
						|
        </label>
 | 
						|
 | 
						|
        <div className='poll__cancel'>
 | 
						|
          <IconButton disabled={index <= 1} title={intl.formatMessage(messages.remove_option)} icon='times' iconComponent={CloseIcon} onClick={this.handleOptionRemove} />
 | 
						|
        </div>
 | 
						|
      </li>
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
const Option = injectIntl(OptionIntl);
 | 
						|
 | 
						|
class PollForm extends ImmutablePureComponent {
 | 
						|
 | 
						|
  static propTypes = {
 | 
						|
    options: ImmutablePropTypes.list,
 | 
						|
    lang: PropTypes.string,
 | 
						|
    expiresIn: PropTypes.number,
 | 
						|
    isMultiple: PropTypes.bool,
 | 
						|
    onChangeOption: PropTypes.func.isRequired,
 | 
						|
    onAddOption: PropTypes.func.isRequired,
 | 
						|
    onRemoveOption: PropTypes.func.isRequired,
 | 
						|
    onChangeSettings: PropTypes.func.isRequired,
 | 
						|
    suggestions: ImmutablePropTypes.list,
 | 
						|
    onClearSuggestions: PropTypes.func.isRequired,
 | 
						|
    onFetchSuggestions: PropTypes.func.isRequired,
 | 
						|
    onSuggestionSelected: PropTypes.func.isRequired,
 | 
						|
    intl: PropTypes.object.isRequired,
 | 
						|
  };
 | 
						|
 | 
						|
  handleAddOption = () => {
 | 
						|
    this.props.onAddOption('');
 | 
						|
  };
 | 
						|
 | 
						|
  handleSelectDuration = e => {
 | 
						|
    this.props.onChangeSettings(e.target.value, this.props.isMultiple);
 | 
						|
  };
 | 
						|
 | 
						|
  handleToggleMultiple = () => {
 | 
						|
    this.props.onChangeSettings(this.props.expiresIn, !this.props.isMultiple);
 | 
						|
  };
 | 
						|
 | 
						|
  render () {
 | 
						|
    const { options, lang, expiresIn, isMultiple, onChangeOption, onRemoveOption, intl, ...other } = this.props;
 | 
						|
 | 
						|
    if (!options) {
 | 
						|
      return null;
 | 
						|
    }
 | 
						|
 | 
						|
    const autoFocusIndex = options.indexOf('');
 | 
						|
 | 
						|
    return (
 | 
						|
      <div className='compose-form__poll-wrapper'>
 | 
						|
        <ul>
 | 
						|
          {options.map((title, i) => <Option title={title} lang={lang} key={i} index={i} onChange={onChangeOption} onRemove={onRemoveOption} isPollMultiple={isMultiple} onToggleMultiple={this.handleToggleMultiple} autoFocus={i === autoFocusIndex} {...other} />)}
 | 
						|
        </ul>
 | 
						|
 | 
						|
        <div className='poll__footer'>
 | 
						|
          <button type='button' disabled={options.size >= 5} className='button button-secondary' onClick={this.handleAddOption}><Icon id='plus' icon={AddIcon} /> <FormattedMessage {...messages.add_option} /></button>
 | 
						|
 | 
						|
          {/* eslint-disable-next-line jsx-a11y/no-onchange */}
 | 
						|
          <select value={expiresIn} onChange={this.handleSelectDuration}>
 | 
						|
            <option value={300}>{intl.formatMessage(messages.minutes, { number: 5 })}</option>
 | 
						|
            <option value={1800}>{intl.formatMessage(messages.minutes, { number: 30 })}</option>
 | 
						|
            <option value={3600}>{intl.formatMessage(messages.hours, { number: 1 })}</option>
 | 
						|
            <option value={21600}>{intl.formatMessage(messages.hours, { number: 6 })}</option>
 | 
						|
            <option value={43200}>{intl.formatMessage(messages.hours, { number: 12 })}</option>
 | 
						|
            <option value={86400}>{intl.formatMessage(messages.days, { number: 1 })}</option>
 | 
						|
            <option value={259200}>{intl.formatMessage(messages.days, { number: 3 })}</option>
 | 
						|
            <option value={604800}>{intl.formatMessage(messages.days, { number: 7 })}</option>
 | 
						|
          </select>
 | 
						|
        </div>
 | 
						|
      </div>
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
export default injectIntl(PollForm);
 |