diff options
Diffstat (limited to 'app/javascript/flavours/glitch/features/composer')
4 files changed, 118 insertions, 33 deletions
diff --git a/app/javascript/flavours/glitch/features/composer/index.js b/app/javascript/flavours/glitch/features/composer/index.js index 3582dedfe..cae9bf9f2 100644 --- a/app/javascript/flavours/glitch/features/composer/index.js +++ b/app/javascript/flavours/glitch/features/composer/index.js @@ -7,6 +7,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import { cancelReplyCompose, changeCompose, + changeComposeAdvancedOption, changeComposeSensitivity, changeComposeSpoilerText, changeComposeSpoilerness, @@ -18,7 +19,6 @@ import { mountCompose, selectComposeSuggestion, submitCompose, - toggleComposeAdvancedOption, undoUploadCompose, unmountCompose, uploadCompose, @@ -49,8 +49,8 @@ function mapStateToProps (state) { const inReplyTo = state.getIn(['compose', 'in_reply_to']); return { acceptContentTypes: state.getIn(['media_attachments', 'accept_content_types']).toArray().join(','), + advancedOptions: state.getIn(['compose', 'advanced_options']), amUnlocked: !state.getIn(['accounts', me, 'locked']), - doNotFederate: state.getIn(['compose', 'advanced_options', 'do_not_federate']), focusDate: state.getIn(['compose', 'focusDate']), isSubmitting: state.getIn(['compose', 'is_submitting']), isUploading: state.getIn(['compose', 'is_uploading']), @@ -76,6 +76,7 @@ function mapStateToProps (state) { // Dispatch mapping. const mapDispatchToProps = { onCancelReply: cancelReplyCompose, + onChangeAdvancedOption: changeComposeAdvancedOption, onChangeDescription: changeUploadCompose, onChangeSensitivity: changeComposeSensitivity, onChangeSpoilerText: changeComposeSpoilerText, @@ -91,7 +92,6 @@ const mapDispatchToProps = { onOpenDoodleModal: openModal.bind(null, 'DOODLE', { noEsc: true }), onSelectSuggestion: selectComposeSuggestion, onSubmit: submitCompose, - onToggleAdvancedOption: toggleComposeAdvancedOption, onUndoUpload: undoUploadCompose, onUnmount: unmountCompose, onUpload: uploadCompose, @@ -267,14 +267,15 @@ class Composer extends React.Component { } = this.handlers; const { acceptContentTypes, + advancedOptions, amUnlocked, - doNotFederate, intl, isSubmitting, isUploading, layout, media, onCancelReply, + onChangeAdvancedOption, onChangeDescription, onChangeSensitivity, onChangeSpoilerness, @@ -285,7 +286,6 @@ class Composer extends React.Component { onFetchSuggestions, onOpenActionsModal, onOpenDoodleModal, - onToggleAdvancedOption, onUndoUpload, onUpload, privacy, @@ -321,6 +321,7 @@ class Composer extends React.Component { /> ) : null} <ComposerTextarea + advancedOptions={advancedOptions} autoFocus={!showSearch && !isMobile(window.innerWidth, layout)} disabled={isSubmitting} intl={intl} @@ -347,19 +348,19 @@ class Composer extends React.Component { ) : null} <ComposerOptions acceptContentTypes={acceptContentTypes} + advancedOptions={advancedOptions} disabled={isSubmitting} - doNotFederate={doNotFederate} full={media.size >= 4 || media.some( item => item.get('type') === 'video' )} hasMedia={!!media.size} intl={intl} + onChangeAdvancedOption={onChangeAdvancedOption} onChangeSensitivity={onChangeSensitivity} onChangeVisibility={onChangeVisibility} onDoodleOpen={onOpenDoodleModal} onModalClose={onCloseModal} onModalOpen={onOpenActionsModal} - onToggleAdvancedOption={onToggleAdvancedOption} onToggleSpoiler={onChangeSpoilerness} onUpload={onUpload} privacy={privacy} @@ -368,7 +369,7 @@ class Composer extends React.Component { spoiler={spoiler} /> <ComposerPublisher - countText={`${spoilerText}${countableText(text)}${doNotFederate ? ' 👁️' : ''}`} + countText={`${spoilerText}${countableText(text)}${advancedOptions.get('do_not_federate') ? ' 👁️' : ''}`} disabled={isSubmitting || isUploading || !!text.length && !text.trim().length} intl={intl} onSecondarySubmit={handleSecondarySubmit} @@ -388,8 +389,8 @@ Composer.propTypes = { // State props. acceptContentTypes: PropTypes.string, + advancedOptions: ImmutablePropTypes.map, amUnlocked: PropTypes.bool, - doNotFederate: PropTypes.bool, focusDate: PropTypes.instanceOf(Date), isSubmitting: PropTypes.bool, isUploading: PropTypes.bool, @@ -412,6 +413,7 @@ Composer.propTypes = { // Dispatch props. onCancelReply: PropTypes.func, + onChangeAdvancedOption: PropTypes.func, onChangeDescription: PropTypes.func, onChangeSensitivity: PropTypes.func, onChangeSpoilerText: PropTypes.func, @@ -427,7 +429,6 @@ Composer.propTypes = { onOpenDoodleModal: PropTypes.func, onSelectSuggestion: PropTypes.func, onSubmit: PropTypes.func, - onToggleAdvancedOption: PropTypes.func, onUndoUpload: PropTypes.func, onUnmount: PropTypes.func, onUpload: PropTypes.func, diff --git a/app/javascript/flavours/glitch/features/composer/options/index.js b/app/javascript/flavours/glitch/features/composer/options/index.js index e805372ab..954508c11 100644 --- a/app/javascript/flavours/glitch/features/composer/options/index.js +++ b/app/javascript/flavours/glitch/features/composer/options/index.js @@ -1,6 +1,7 @@ // Package imports. import PropTypes from 'prop-types'; import React from 'react'; +import ImmutablePropTypes from 'react-immutable-proptypes'; import { FormattedMessage, defineMessages, @@ -47,11 +48,11 @@ const messages = defineMessages({ }, local_only_long: { defaultMessage: 'Do not post to other instances', - id: 'advanced-options.local-only.long', + id: 'advanced_options.local-only.long', }, local_only_short: { defaultMessage: 'Local-only', - id: 'advanced-options.local-only.short', + id: 'advanced_options.local-only.short', }, private_long: { defaultMessage: 'Post to followers only', @@ -77,6 +78,14 @@ const messages = defineMessages({ defaultMessage: 'Hide text behind warning', id: 'compose_form.spoiler', }, + threaded_mode_long: { + defaultMessage: 'Automatically opens a reply on posting', + id: 'advanced_options.threaded_mode.long', + }, + threaded_mode_short: { + defaultMessage: 'Threaded mode', + id: 'advanced_options.threaded_mode.short', + }, unlisted_long: { defaultMessage: 'Do not show in public timelines', id: 'privacy.unlisted.long', @@ -149,16 +158,16 @@ export default class ComposerOptions extends React.PureComponent { } = this.handlers; const { acceptContentTypes, + advancedOptions, disabled, - doNotFederate, full, hasMedia, intl, + onChangeAdvancedOption, onChangeSensitivity, onChangeVisibility, onModalClose, onModalOpen, - onToggleAdvancedOption, onToggleSpoiler, privacy, resetFileKey, @@ -283,23 +292,31 @@ export default class ComposerOptions extends React.PureComponent { onClick={onToggleSpoiler} title={intl.formatMessage(messages.spoiler)} /> - <Dropdown - active={doNotFederate} - disabled={disabled} - icon='home' - items={[ - { - meta: <FormattedMessage {...messages.local_only_long} />, - name: 'do_not_federate', - on: doNotFederate, - text: <FormattedMessage {...messages.local_only_short} />, - }, - ]} - onChange={onToggleAdvancedOption} - onModalClose={onModalClose} - onModalOpen={onModalOpen} - title={intl.formatMessage(messages.advanced_options_icon_title)} - /> + {advancedOptions ? ( + <Dropdown + active={advancedOptions.some(value => !!value)} + disabled={disabled} + icon='ellipsis-h' + items={[ + { + meta: <FormattedMessage {...messages.local_only_long} />, + name: 'do_not_federate', + on: advancedOptions.get('do_not_federate'), + text: <FormattedMessage {...messages.local_only_short} />, + }, + { + meta: <FormattedMessage {...messages.threaded_mode_long} />, + name: 'threaded_mode', + on: advancedOptions.get('threaded_mode'), + text: <FormattedMessage {...messages.threaded_mode_short} />, + }, + ]} + onChange={onChangeAdvancedOption} + onModalClose={onModalClose} + onModalOpen={onModalOpen} + title={intl.formatMessage(messages.advanced_options_icon_title)} + /> + ) : null} </div> ); } @@ -309,17 +326,17 @@ export default class ComposerOptions extends React.PureComponent { // Props. ComposerOptions.propTypes = { acceptContentTypes: PropTypes.string, + advancedOptions: ImmutablePropTypes.map, disabled: PropTypes.bool, - doNotFederate: PropTypes.bool, full: PropTypes.bool, hasMedia: PropTypes.bool, intl: PropTypes.object.isRequired, + onChangeAdvancedOption: PropTypes.func, onChangeSensitivity: PropTypes.func, onChangeVisibility: PropTypes.func, onDoodleOpen: PropTypes.func, onModalClose: PropTypes.func, onModalOpen: PropTypes.func, - onToggleAdvancedOption: PropTypes.func, onToggleSpoiler: PropTypes.func, onUpload: PropTypes.func, privacy: PropTypes.string, diff --git a/app/javascript/flavours/glitch/features/composer/textarea/icons/index.js b/app/javascript/flavours/glitch/features/composer/textarea/icons/index.js new file mode 100644 index 000000000..049cdd5cd --- /dev/null +++ b/app/javascript/flavours/glitch/features/composer/textarea/icons/index.js @@ -0,0 +1,60 @@ +// Package imports. +import PropTypes from 'prop-types'; +import React from 'react'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import { defineMessages } from 'react-intl'; + +// Components. +import Icon from 'flavours/glitch/components/icon'; + +// Messages. +const messages = defineMessages({ + localOnly: { + defaultMessage: 'This post is local-only', + id: 'advanced_options.local-only.tooltip', + }, + threadedMode: { + defaultMessage: 'Threaded mode enabled', + id: 'advanced_options.threaded_mode.tooltip', + }, +}); + +// We use an array of tuples here instead of an object because it +// preserves order. +const iconMap = [ + ['do_not_federate', 'home', messages.localOnly], + ['threaded_mode', 'comments', messages.threadedMode], +]; + +// The component. +export default function ComposerTextareaIcons ({ + advancedOptions, + intl, +}) { + + // The result. We just map every active option to its icon. + return ( + <div className='composer--textarea--icons'> + {advancedOptions ? iconMap.map( + ([key, icon, message]) => advancedOptions.get(key) ? ( + <span + className='textarea_icon' + key={key} + title={intl.formatMessage(message)} + > + <Icon + fullwidth + icon={icon} + /> + </span> + ) : null + ) : null} + </div> + ); +} + +// Props. +ComposerTextareaIcons.propTypes = { + advancedOptions: ImmutablePropTypes.map, + intl: PropTypes.object.isRequired, +}; diff --git a/app/javascript/flavours/glitch/features/composer/textarea/index.js b/app/javascript/flavours/glitch/features/composer/textarea/index.js index 2e0b3e3d7..0f5fd4d4d 100644 --- a/app/javascript/flavours/glitch/features/composer/textarea/index.js +++ b/app/javascript/flavours/glitch/features/composer/textarea/index.js @@ -10,6 +10,7 @@ import Textarea from 'react-textarea-autosize'; // Components. import EmojiPicker from 'flavours/glitch/features/emoji_picker'; +import ComposerTextareaIcons from './icons'; import ComposerTextareaSuggestions from './suggestions'; // Utils. @@ -232,6 +233,7 @@ export default class ComposerTextarea extends React.Component { handleRefTextarea, } = this.handlers; const { + advancedOptions, autoFocus, disabled, intl, @@ -249,6 +251,10 @@ export default class ComposerTextarea extends React.Component { <div className='composer--textarea'> <label> <span {...hiddenComponent}><FormattedMessage {...messages.placeholder} /></span> + <ComposerTextareaIcons + advancedOptions={advancedOptions} + intl={intl} + /> <Textarea aria-autocomplete='list' autoFocus={autoFocus} @@ -280,6 +286,7 @@ export default class ComposerTextarea extends React.Component { // Props. ComposerTextarea.propTypes = { + advancedOptions: ImmutablePropTypes.map, autoFocus: PropTypes.bool, disabled: PropTypes.bool, intl: PropTypes.object.isRequired, |