From 8fe86cebaa30e77e50c8223a1ff83759dbd7ca62 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Wed, 6 Mar 2019 12:30:11 +0100 Subject: [Glitch] Port polls creation UI from upstream --- .../flavours/glitch/features/composer/index.js | 52 ++++++--- .../glitch/features/composer/options/index.js | 35 +++++- .../composer/poll_form/components/poll_form.js | 121 +++++++++++++++++++++ .../glitch/features/composer/poll_form/index.js | 29 +++++ 4 files changed, 218 insertions(+), 19 deletions(-) create mode 100644 app/javascript/flavours/glitch/features/composer/poll_form/components/poll_form.js create mode 100644 app/javascript/flavours/glitch/features/composer/poll_form/index.js (limited to 'app/javascript/flavours/glitch/features/composer') diff --git a/app/javascript/flavours/glitch/features/composer/index.js b/app/javascript/flavours/glitch/features/composer/index.js index ec0e405a4..9d2e0b3da 100644 --- a/app/javascript/flavours/glitch/features/composer/index.js +++ b/app/javascript/flavours/glitch/features/composer/index.js @@ -31,6 +31,7 @@ import { openModal, } from 'flavours/glitch/actions/modal'; import { changeLocalSetting } from 'flavours/glitch/actions/local_settings'; +import { addPoll, removePoll } from 'flavours/glitch/actions/compose'; // Components. import ComposerOptions from './options'; @@ -39,6 +40,7 @@ import ComposerReply from './reply'; import ComposerSpoiler from './spoiler'; import ComposerTextarea from './textarea'; import ComposerUploadForm from './upload_form'; +import ComposerPollForm from './poll_form'; import ComposerWarning from './warning'; import ComposerHashtagWarning from './hashtag_warning'; import ComposerDirectWarning from './direct_warning'; @@ -102,6 +104,7 @@ function mapStateToProps (state) { suggestions: state.getIn(['compose', 'suggestions']), text: state.getIn(['compose', 'text']), anyMedia: state.getIn(['compose', 'media_attachments']).size > 0, + poll: state.getIn(['compose', 'poll']), spoilersAlwaysOn: spoilersAlwaysOn, mediaDescriptionConfirmation: state.getIn(['local_settings', 'confirm_missing_media_description']), preselectOnReply: state.getIn(['local_settings', 'preselect_on_reply']), @@ -134,6 +137,15 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ onChangeVisibility(value) { dispatch(changeComposeVisibility(value)); }, + onTogglePoll() { + dispatch((_, getState) => { + if (getState().getIn(['compose', 'poll'])) { + dispatch(removePoll()); + } else { + dispatch(addPoll()); + } + }); + }, onClearSuggestions() { dispatch(clearComposeSuggestions()); }, @@ -394,6 +406,7 @@ class Composer extends React.Component { isUploading, layout, media, + poll, onCancelReply, onChangeAdvancedOption, onChangeDescription, @@ -401,6 +414,7 @@ class Composer extends React.Component { onChangeSpoilerness, onChangeText, onChangeVisibility, + onTogglePoll, onClearSuggestions, onCloseModal, onFetchSuggestions, @@ -463,30 +477,38 @@ class Composer extends React.Component { suggestions={suggestions} value={text} /> - {isUploading || media && media.size ? ( - - ) : null} +
+ {isUploading || media && media.size ? ( + + ) : null} + {!!poll && ( + + )} +
= 4 || media.some( - item => item.get('type') === 'video' - ) : false} + allowMedia={!poll && (media ? media.size < 4 && !media.some( + item => item.get('type') === 'video' + ) : true)} hasMedia={media && !!media.size} + allowPoll={!(media && !!media.size)} + hasPoll={!!poll} intl={intl} onChangeAdvancedOption={onChangeAdvancedOption} onChangeSensitivity={onChangeSensitivity} onChangeVisibility={onChangeVisibility} + onTogglePoll={onTogglePoll} onDoodleOpen={onOpenDoodleModal} onModalClose={onCloseModal} onModalOpen={onOpenActionsModal} diff --git a/app/javascript/flavours/glitch/features/composer/options/index.js b/app/javascript/flavours/glitch/features/composer/options/index.js index 5b4a7444c..80ac1b63a 100644 --- a/app/javascript/flavours/glitch/features/composer/options/index.js +++ b/app/javascript/flavours/glitch/features/composer/options/index.js @@ -98,6 +98,14 @@ const messages = defineMessages({ defaultMessage: 'Upload a file', id: 'compose.attach.upload', }, + add_poll: { + defaultMessage: 'Add a poll', + id: 'poll_button.add_poll', + }, + remove_poll: { + defaultMessage: 'Remove poll', + id: 'poll_button.remove_poll', + }, }); // Handlers. @@ -160,12 +168,15 @@ export default class ComposerOptions extends React.PureComponent { acceptContentTypes, advancedOptions, disabled, - full, + allowMedia, hasMedia, + allowPoll, + hasPoll, intl, onChangeAdvancedOption, onChangeSensitivity, onChangeVisibility, + onTogglePoll, onModalClose, onModalOpen, onToggleSpoiler, @@ -209,7 +220,7 @@ export default class ComposerOptions extends React.PureComponent {
+ { + this.props.onChange(this.props.index, e.target.value); + }; + + handleOptionRemove = () => { + this.props.onRemove(this.props.index); + }; + + render () { + const { isPollMultiple, title, index, intl } = this.props; + + return ( +
  • + + +
    + +
    +
  • + ); + } + +} + +export default +@injectIntl +class PollForm extends ImmutablePureComponent { + + static propTypes = { + options: ImmutablePropTypes.list, + expiresIn: PropTypes.number, + isMultiple: PropTypes.bool, + onChangeOption: PropTypes.func.isRequired, + onAddOption: PropTypes.func.isRequired, + onRemoveOption: PropTypes.func.isRequired, + onChangeSettings: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + }; + + handleAddOption = () => { + this.props.onAddOption(''); + }; + + handleSelectDuration = e => { + this.props.onChangeSettings(e.target.value, this.props.isMultiple); + }; + + render () { + const { options, expiresIn, isMultiple, onChangeOption, onRemoveOption, intl } = this.props; + + if (!options) { + return null; + } + + return ( +
    +
      + {options.map((title, i) =>
    + +
    + {options.size < 4 && ( + + )} + + +
    +
    + ); + } + +} diff --git a/app/javascript/flavours/glitch/features/composer/poll_form/index.js b/app/javascript/flavours/glitch/features/composer/poll_form/index.js new file mode 100644 index 000000000..5232c3b31 --- /dev/null +++ b/app/javascript/flavours/glitch/features/composer/poll_form/index.js @@ -0,0 +1,29 @@ +import { connect } from 'react-redux'; +import PollForm from './components/poll_form'; +import { addPollOption, removePollOption, changePollOption, changePollSettings } from '../../../actions/compose'; + +const mapStateToProps = state => ({ + options: state.getIn(['compose', 'poll', 'options']), + expiresIn: state.getIn(['compose', 'poll', 'expires_in']), + isMultiple: state.getIn(['compose', 'poll', 'multiple']), +}); + +const mapDispatchToProps = dispatch => ({ + onAddOption(title) { + dispatch(addPollOption(title)); + }, + + onRemoveOption(index) { + dispatch(removePollOption(index)); + }, + + onChangeOption(index, title) { + dispatch(changePollOption(index, title)); + }, + + onChangeSettings(expiresIn, isMultiple) { + dispatch(changePollSettings(expiresIn, isMultiple)); + }, +}); + +export default connect(mapStateToProps, mapDispatchToProps)(PollForm); -- cgit From 235be596bc6a6ed6a042975ac7f698695d6cda19 Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Wed, 6 Mar 2019 13:42:48 +0100 Subject: Use server-provided poll limits instead of hardcoded ones Also does not enable polls if no limits are provided by the server --- .../glitch/features/composer/options/index.js | 29 ++++++++++++---------- .../composer/poll_form/components/poll_form.js | 5 ++-- .../flavours/glitch/util/initial_state.js | 1 + 3 files changed, 20 insertions(+), 15 deletions(-) (limited to 'app/javascript/flavours/glitch/features/composer') diff --git a/app/javascript/flavours/glitch/features/composer/options/index.js b/app/javascript/flavours/glitch/features/composer/options/index.js index 80ac1b63a..7c7f01dc2 100644 --- a/app/javascript/flavours/glitch/features/composer/options/index.js +++ b/app/javascript/flavours/glitch/features/composer/options/index.js @@ -19,6 +19,7 @@ import { assignHandlers, hiddenComponent, } from 'flavours/glitch/util/react_helpers'; +import { pollLimits } from 'flavours/glitch/util/initial_state'; // Messages. const messages = defineMessages({ @@ -248,19 +249,21 @@ export default class ComposerOptions extends React.PureComponent { onModalOpen={onModalOpen} title={intl.formatMessage(messages.attach)} /> - + {!!pollLimits && ( + + )} @@ -100,7 +101,7 @@ class PollForm extends ImmutablePureComponent {
    - {options.size < 4 && ( + {options.size < pollLimits.max_options && ( )} diff --git a/app/javascript/flavours/glitch/util/initial_state.js b/app/javascript/flavours/glitch/util/initial_state.js index a3c65563c..62588eeaa 100644 --- a/app/javascript/flavours/glitch/util/initial_state.js +++ b/app/javascript/flavours/glitch/util/initial_state.js @@ -22,6 +22,7 @@ export const deleteModal = getMeta('delete_modal'); export const me = getMeta('me'); export const searchEnabled = getMeta('search_enabled'); export const maxChars = (initialState && initialState.max_toot_chars) || 500; +export const pollLimits = (initialState && initialState.poll_limits); export const invitesEnabled = getMeta('invites_enabled'); export const version = getMeta('version'); export const mascot = getMeta('mascot'); -- cgit From 84c807a0dcdfd6c3c18ee37fcb5003a92a92ce3f Mon Sep 17 00:00:00 2001 From: Thibaut Girka Date: Wed, 6 Mar 2019 14:18:29 +0100 Subject: Allow setting whether this is a single choice poll or multiple choices one --- .../composer/poll_form/components/poll_form.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'app/javascript/flavours/glitch/features/composer') diff --git a/app/javascript/flavours/glitch/features/composer/poll_form/components/poll_form.js b/app/javascript/flavours/glitch/features/composer/poll_form/components/poll_form.js index 566cf123a..7ee28e304 100644 --- a/app/javascript/flavours/glitch/features/composer/poll_form/components/poll_form.js +++ b/app/javascript/flavours/glitch/features/composer/poll_form/components/poll_form.js @@ -13,6 +13,8 @@ const messages = defineMessages({ 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' }, + single_choice: { id: 'compose_form.poll.single_choice', defaultMessage: 'Allow one choice' }, + multiple_choices: { id: 'compose_form.poll.multiple_choices', defaultMessage: 'Allow multiple choices' }, 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}}' }, @@ -87,6 +89,10 @@ class PollForm extends ImmutablePureComponent { this.props.onChangeSettings(e.target.value, this.props.isMultiple); }; + handleSelectMultiple = e => { + this.props.onChangeSettings(this.props.expiresIn, e.target.value === 'true'); + }; + render () { const { options, expiresIn, isMultiple, onChangeOption, onRemoveOption, intl } = this.props; @@ -98,12 +104,19 @@ class PollForm extends ImmutablePureComponent {
      {options.map((title, i) =>
    - {options.size < pollLimits.max_options && ( - - )} +