diff options
author | Thibaut Girka <thib@sitedethib.com> | 2019-04-21 12:09:52 +0200 |
---|---|---|
committer | ThibG <thib@sitedethib.com> | 2019-04-22 20:15:47 +0200 |
commit | a243567a3e6100d65477162308e2c1bb5e056c21 (patch) | |
tree | e308927e5453acd62e09f6b6de05c269c362d5b8 /app/javascript/flavours/glitch/features/compose | |
parent | c5f49a92dce9157debf3a68487dd30b6f0af6c4a (diff) |
ComposerUploadForm → UploadForm + UploadFormContainer
Diffstat (limited to 'app/javascript/flavours/glitch/features/compose')
8 files changed, 251 insertions, 38 deletions
diff --git a/app/javascript/flavours/glitch/features/compose/components/compose_form.js b/app/javascript/flavours/glitch/features/compose/components/compose_form.js index ccbcba571..ecd1aed69 100644 --- a/app/javascript/flavours/glitch/features/compose/components/compose_form.js +++ b/app/javascript/flavours/glitch/features/compose/components/compose_form.js @@ -8,7 +8,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import ComposerOptions from '../../composer/options'; import ComposerPublisher from '../../composer/publisher'; import ComposerTextarea from '../../composer/textarea'; -import ComposerUploadForm from '../../composer/upload_form'; +import UploadFormContainer from '../containers/upload_form_container'; import PollFormContainer from '../containers/poll_form_container'; import WarningContainer from '../containers/warning_container'; import ReplyIndicatorContainer from '../containers/reply_indicator_container'; @@ -48,7 +48,6 @@ class ComposeForm extends ImmutablePureComponent { media: ImmutablePropTypes.list, preselectDate: PropTypes.instanceOf(Date), privacy: PropTypes.string, - progress: PropTypes.number, resetFileKey: PropTypes.number, sideArm: PropTypes.string, sensitive: PropTypes.bool, @@ -65,7 +64,6 @@ class ComposeForm extends ImmutablePureComponent { // Dispatch props. onChangeAdvancedOption: PropTypes.func, - onChangeDescription: PropTypes.func, onChangeSensitivity: PropTypes.func, onChangeSpoilerText: PropTypes.func, onChangeSpoilerness: PropTypes.func, @@ -80,7 +78,6 @@ class ComposeForm extends ImmutablePureComponent { onOpenDoodleModal: PropTypes.func, onSelectSuggestion: PropTypes.func, onSubmit: PropTypes.func, - onUndoUpload: PropTypes.func, onUnmount: PropTypes.func, onUpload: PropTypes.func, onMediaDescriptionConfirm: PropTypes.func, @@ -185,11 +182,6 @@ class ComposeForm extends ImmutablePureComponent { } } - // Sets a reference to the upload form. - handleRefUploadForm = (uploadFormComponent) => { - this.uploadForm = uploadFormComponent; - } - // Sets a reference to the textarea. handleRefTextarea = (textareaComponent) => { if (textareaComponent) { @@ -283,7 +275,6 @@ class ComposeForm extends ImmutablePureComponent { handleSecondarySubmit, handleSelect, handleSubmit, - handleRefUploadForm, handleRefTextarea, } = this; const { @@ -299,7 +290,6 @@ class ComposeForm extends ImmutablePureComponent { media, poll, onChangeAdvancedOption, - onChangeDescription, onChangeSensitivity, onChangeSpoilerness, onChangeText, @@ -310,11 +300,8 @@ class ComposeForm extends ImmutablePureComponent { onFetchSuggestions, onOpenActionsModal, onOpenDoodleModal, - onOpenFocalPointModal, - onUndoUpload, onUpload, privacy, - progress, resetFileKey, sensitive, showSearch, @@ -370,18 +357,7 @@ class ComposeForm extends ImmutablePureComponent { value={text} /> <div className='compose-form__modifiers'> - {isUploading || media && media.size ? ( - <ComposerUploadForm - intl={intl} - media={media} - onChangeDescription={onChangeDescription} - onOpenFocalPointModal={onOpenFocalPointModal} - onRemove={onUndoUpload} - progress={progress} - uploading={isUploading} - handleRef={handleRefUploadForm} - /> - ) : null} + <UploadFormContainer /> <PollFormContainer /> </div> <ComposerOptions diff --git a/app/javascript/flavours/glitch/features/compose/components/upload.js b/app/javascript/flavours/glitch/features/compose/components/upload.js new file mode 100644 index 000000000..84edf664e --- /dev/null +++ b/app/javascript/flavours/glitch/features/compose/components/upload.js @@ -0,0 +1,131 @@ +import React from 'react'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import PropTypes from 'prop-types'; +import Motion from 'flavours/glitch/util/optional_motion'; +import spring from 'react-motion/lib/spring'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import classNames from 'classnames'; +import Icon from 'flavours/glitch/components/icon'; +import { isUserTouching } from 'flavours/glitch/util/is_mobile'; + +const messages = defineMessages({ + description: { id: 'upload_form.description', defaultMessage: 'Describe for the visually impaired' }, +}); + +// The component. +export default @injectIntl +class Upload extends ImmutablePureComponent { + + static contextTypes = { + router: PropTypes.object, + }; + + static propTypes = { + media: ImmutablePropTypes.map.isRequired, + intl: PropTypes.object.isRequired, + onUndo: PropTypes.func.isRequired, + onDescriptionChange: PropTypes.func.isRequired, + onOpenFocalPoint: PropTypes.func.isRequired, + onSubmit: PropTypes.func.isRequired, + }; + + state = { + hovered: false, + focused: false, + dirtyDescription: null, + }; + + handleKeyDown = (e) => { + if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) { + this.handleSubmit(); + } + } + + handleSubmit = () => { + this.handleInputBlur(); + this.props.onSubmit(this.context.router.history); + } + + handleUndoClick = e => { + e.stopPropagation(); + this.props.onUndo(this.props.media.get('id')); + } + + handleFocalPointClick = e => { + e.stopPropagation(); + this.props.onOpenFocalPoint(this.props.media.get('id')); + } + + handleInputChange = e => { + this.setState({ dirtyDescription: e.target.value }); + } + + handleMouseEnter = () => { + this.setState({ hovered: true }); + } + + handleMouseLeave = () => { + this.setState({ hovered: false }); + } + + handleInputFocus = () => { + this.setState({ focused: true }); + } + + handleClick = () => { + this.setState({ focused: true }); + } + + handleInputBlur = () => { + const { dirtyDescription } = this.state; + + this.setState({ focused: false, dirtyDescription: null }); + + if (dirtyDescription !== null) { + this.props.onDescriptionChange(this.props.media.get('id'), dirtyDescription); + } + } + + render () { + const { intl, media } = this.props; + const active = this.state.hovered || this.state.focused || isUserTouching(); + const description = this.state.dirtyDescription || (this.state.dirtyDescription !== '' && media.get('description')) || ''; + const computedClass = classNames('composer--upload_form--item', { active }); + const focusX = media.getIn(['meta', 'focus', 'x']); + const focusY = media.getIn(['meta', 'focus', 'y']); + const x = ((focusX / 2) + .5) * 100; + const y = ((focusY / -2) + .5) * 100; + + return ( + <div className={computedClass} tabIndex='0' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} onClick={this.handleClick} role='button'> + <Motion defaultStyle={{ scale: 0.8 }} style={{ scale: spring(1, { stiffness: 180, damping: 12, }) }}> + {({ scale }) => ( + <div style={{ transform: `scale(${scale})`, backgroundImage: `url(${media.get('preview_url')})`, backgroundPosition: `${x}% ${y}%` }}> + <div className={classNames('composer--upload_form--actions', { active })}> + <button className='icon-button' onClick={this.handleUndoClick}><Icon icon='times' /> <FormattedMessage id='upload_form.undo' defaultMessage='Delete' /></button> + {media.get('type') === 'image' && <button className='icon-button' onClick={this.handleFocalPointClick}><Icon id='crosshairs' /> <FormattedMessage id='upload_form.focus' defaultMessage='Crop' /></button>} + </div> + + <div className={classNames('composer--upload_form--description', { active })}> + <label> + <span style={{ display: 'none' }}>{intl.formatMessage(messages.description)}</span> + <textarea + placeholder={intl.formatMessage(messages.description)} + value={description} + maxLength={420} + onFocus={this.handleInputFocus} + onChange={this.handleInputChange} + onBlur={this.handleInputBlur} + onKeyDown={this.handleKeyDown} + /> + </label> + </div> + </div> + )} + </Motion> + </div> + ); + } + +} diff --git a/app/javascript/flavours/glitch/features/compose/components/upload_form.js b/app/javascript/flavours/glitch/features/compose/components/upload_form.js new file mode 100644 index 000000000..a126cc7e4 --- /dev/null +++ b/app/javascript/flavours/glitch/features/compose/components/upload_form.js @@ -0,0 +1,28 @@ +import React from 'react'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import UploadProgressContainer from '../containers/upload_progress_container'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import UploadContainer from '../containers/upload_container'; + +export default class UploadForm extends ImmutablePureComponent { + static propTypes = { + mediaIds: ImmutablePropTypes.list.isRequired, + }; + + render () { + const { mediaIds } = this.props; + + return ( + <div className='composer--upload_form'> + <UploadProgressContainer /> + + <div className='content'> + {mediaIds.map(id => ( + <UploadContainer id={id} key={id} /> + ))} + </div> + </div> + ); + } + +} diff --git a/app/javascript/flavours/glitch/features/compose/components/upload_progress.js b/app/javascript/flavours/glitch/features/compose/components/upload_progress.js new file mode 100644 index 000000000..264c563f2 --- /dev/null +++ b/app/javascript/flavours/glitch/features/compose/components/upload_progress.js @@ -0,0 +1,42 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import Motion from 'flavours/glitch/util/optional_motion'; +import spring from 'react-motion/lib/spring'; +import { FormattedMessage } from 'react-intl'; +import Icon from 'flavours/glitch/components/icon'; + +export default class UploadProgress extends React.PureComponent { + + static propTypes = { + active: PropTypes.bool, + progress: PropTypes.number, + }; + + render () { + const { active, progress } = this.props; + + if (!active) { + return null; + } + + return ( + <div className='composer--upload_form--progress'> + <Icon icon='upload' /> + + <div className='message'> + <FormattedMessage id='upload_progress.label' defaultMessage='Uploading...' /> + + <div className='backdrop'> + <Motion defaultStyle={{ width: 0 }} style={{ width: spring(progress) }}> + {({ width }) => + (<div className='tracker' style={{ width: `${width}%` }} + />) + } + </Motion> + </div> + </div> + </div> + ); + } + +} diff --git a/app/javascript/flavours/glitch/features/compose/containers/compose_form_container.js b/app/javascript/flavours/glitch/features/compose/containers/compose_form_container.js index 3293cc226..4716d9435 100644 --- a/app/javascript/flavours/glitch/features/compose/containers/compose_form_container.js +++ b/app/javascript/flavours/glitch/features/compose/containers/compose_form_container.js @@ -7,14 +7,12 @@ import { changeComposeSpoilerText, changeComposeSpoilerness, changeComposeVisibility, - changeUploadCompose, clearComposeSuggestions, fetchComposeSuggestions, insertEmojiCompose, mountCompose, selectComposeSuggestion, submitCompose, - undoUploadCompose, unmountCompose, uploadCompose, } from 'flavours/glitch/actions/compose'; @@ -66,7 +64,6 @@ function mapStateToProps (state) { media: state.getIn(['compose', 'media_attachments']), preselectDate: state.getIn(['compose', 'preselectDate']), privacy: state.getIn(['compose', 'privacy']), - progress: state.getIn(['compose', 'progress']), resetFileKey: state.getIn(['compose', 'resetFileKey']), sideArm: sideArmPrivacy, sensitive: state.getIn(['compose', 'sensitive']), @@ -89,9 +86,6 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ onChangeAdvancedOption(option, value) { dispatch(changeComposeAdvancedOption(option, value)); }, - onChangeDescription(id, description) { - dispatch(changeUploadCompose(id, { description })); - }, onChangeSensitivity() { dispatch(changeComposeSensitivity()); }, @@ -137,9 +131,6 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ onOpenDoodleModal() { dispatch(openModal('DOODLE', { noEsc: true })); }, - onOpenFocalPointModal(id) { - dispatch(openModal('FOCAL_POINT', { id })); - }, onSelectSuggestion(position, token, suggestion) { dispatch(selectComposeSuggestion(position, token, suggestion)); }, @@ -154,9 +145,6 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ onSubmit(routerHistory) { dispatch(submitCompose(routerHistory)); }, - onUndoUpload(id) { - dispatch(undoUploadCompose(id)); - }, onUnmount() { dispatch(unmountCompose()); }, diff --git a/app/javascript/flavours/glitch/features/compose/containers/upload_container.js b/app/javascript/flavours/glitch/features/compose/containers/upload_container.js new file mode 100644 index 000000000..d6bff63ac --- /dev/null +++ b/app/javascript/flavours/glitch/features/compose/containers/upload_container.js @@ -0,0 +1,31 @@ +import { connect } from 'react-redux'; +import Upload from '../components/upload'; +import { undoUploadCompose, changeUploadCompose } from 'flavours/glitch/actions/compose'; +import { openModal } from 'flavours/glitch/actions/modal'; +import { submitCompose } from 'flavours/glitch/actions/compose'; + +const mapStateToProps = (state, { id }) => ({ + media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id), +}); + +const mapDispatchToProps = dispatch => ({ + + onUndo: id => { + dispatch(undoUploadCompose(id)); + }, + + onDescriptionChange: (id, description) => { + dispatch(changeUploadCompose(id, { description })); + }, + + onOpenFocalPoint: id => { + dispatch(openModal('FOCAL_POINT', { id })); + }, + + onSubmit (router) { + dispatch(submitCompose(router)); + }, + +}); + +export default connect(mapStateToProps, mapDispatchToProps)(Upload); diff --git a/app/javascript/flavours/glitch/features/compose/containers/upload_form_container.js b/app/javascript/flavours/glitch/features/compose/containers/upload_form_container.js new file mode 100644 index 000000000..a6798bf51 --- /dev/null +++ b/app/javascript/flavours/glitch/features/compose/containers/upload_form_container.js @@ -0,0 +1,8 @@ +import { connect } from 'react-redux'; +import UploadForm from '../components/upload_form'; + +const mapStateToProps = state => ({ + mediaIds: state.getIn(['compose', 'media_attachments']).map(item => item.get('id')), +}); + +export default connect(mapStateToProps)(UploadForm); diff --git a/app/javascript/flavours/glitch/features/compose/containers/upload_progress_container.js b/app/javascript/flavours/glitch/features/compose/containers/upload_progress_container.js new file mode 100644 index 000000000..0cfee96da --- /dev/null +++ b/app/javascript/flavours/glitch/features/compose/containers/upload_progress_container.js @@ -0,0 +1,9 @@ +import { connect } from 'react-redux'; +import UploadProgress from '../components/upload_progress'; + +const mapStateToProps = state => ({ + active: state.getIn(['compose', 'is_uploading']), + progress: state.getIn(['compose', 'progress']), +}); + +export default connect(mapStateToProps)(UploadProgress); |