From d7c6c6dbe109544911f3fca5c547b55d1e79ccc2 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 24 Mar 2017 03:50:30 +0100 Subject: Fancier drag & drop indicator, emoji icon for emoji, upload progress (fix #295) --- .../features/compose/components/compose_form.jsx | 2 - .../compose/components/emoji_picker_dropdown.jsx | 6 +-- .../features/compose/components/upload_form.jsx | 24 ++++++------ .../compose/components/upload_progress.jsx | 44 ++++++++++++++++++++++ .../compose/containers/compose_form_container.jsx | 1 - .../containers/upload_progress_container.jsx | 9 +++++ .../features/ui/components/upload_area.jsx | 32 ++++++++++++++++ .../javascripts/components/features/ui/index.jsx | 23 ++++++++--- 8 files changed, 118 insertions(+), 23 deletions(-) create mode 100644 app/assets/javascripts/components/features/compose/components/upload_progress.jsx create mode 100644 app/assets/javascripts/components/features/compose/containers/upload_progress_container.jsx create mode 100644 app/assets/javascripts/components/features/ui/components/upload_area.jsx (limited to 'app/assets/javascripts/components/features') diff --git a/app/assets/javascripts/components/features/compose/components/compose_form.jsx b/app/assets/javascripts/components/features/compose/components/compose_form.jsx index 047c974f2..2a252af4b 100644 --- a/app/assets/javascripts/components/features/compose/components/compose_form.jsx +++ b/app/assets/javascripts/components/features/compose/components/compose_form.jsx @@ -34,7 +34,6 @@ const ComposeForm = React.createClass({ private: React.PropTypes.bool, unlisted: React.PropTypes.bool, spoiler_text: React.PropTypes.string, - fileDropDate: React.PropTypes.instanceOf(Date), focusDate: React.PropTypes.instanceOf(Date), preselectDate: React.PropTypes.instanceOf(Date), is_submitting: React.PropTypes.bool, @@ -161,7 +160,6 @@ const ComposeForm = React.createClass({ ref={this.setAutosuggestTextarea} placeholder={intl.formatMessage(messages.placeholder)} disabled={disabled} - fileDropDate={this.props.fileDropDate} value={this.props.text} onChange={this.handleChange} suggestions={this.props.suggestions} diff --git a/app/assets/javascripts/components/features/compose/components/emoji_picker_dropdown.jsx b/app/assets/javascripts/components/features/compose/components/emoji_picker_dropdown.jsx index 3a454a5fb..37e366203 100644 --- a/app/assets/javascripts/components/features/compose/components/emoji_picker_dropdown.jsx +++ b/app/assets/javascripts/components/features/compose/components/emoji_picker_dropdown.jsx @@ -4,7 +4,7 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; import { defineMessages, injectIntl } from 'react-intl'; const messages = defineMessages({ - emoji: { id: 'emoji_button.label', defaultMessage: 'Emoji' } + emoji: { id: 'emoji_button.label', defaultMessage: 'Insert emoji' } }); const settings = { @@ -36,8 +36,8 @@ const EmojiPickerDropdown = React.createClass({ return ( - - + + 🙂 diff --git a/app/assets/javascripts/components/features/compose/components/upload_form.jsx b/app/assets/javascripts/components/features/compose/components/upload_form.jsx index 94c94b4b7..1a01c2380 100644 --- a/app/assets/javascripts/components/features/compose/components/upload_form.jsx +++ b/app/assets/javascripts/components/features/compose/components/upload_form.jsx @@ -2,6 +2,8 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; import ImmutablePropTypes from 'react-immutable-proptypes'; import IconButton from '../../../components/icon_button'; import { defineMessages, injectIntl } from 'react-intl'; +import UploadProgressContainer from '../containers/upload_progress_container'; +import { Motion, spring } from 'react-motion'; const messages = defineMessages({ undo: { id: 'upload_form.undo', defaultMessage: 'Undo' } @@ -11,7 +13,6 @@ const UploadForm = React.createClass({ propTypes: { media: ImmutablePropTypes.list.isRequired, - is_uploading: React.PropTypes.bool, onRemoveFile: React.PropTypes.func.isRequired, intl: React.PropTypes.object.isRequired }, @@ -21,20 +22,21 @@ const UploadForm = React.createClass({ render () { const { intl, media } = this.props; - if (!media.size) { - return null; - } - - const uploads = media.map(attachment => ( -
-
- -
+ const uploads = media.map(attachment => +
+ + {({ scale }) => +
+ +
+ } +
- )); + ); return (
+ {uploads}
); diff --git a/app/assets/javascripts/components/features/compose/components/upload_progress.jsx b/app/assets/javascripts/components/features/compose/components/upload_progress.jsx new file mode 100644 index 000000000..86ffbf936 --- /dev/null +++ b/app/assets/javascripts/components/features/compose/components/upload_progress.jsx @@ -0,0 +1,44 @@ +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import { Motion, spring } from 'react-motion'; +import { FormattedMessage } from 'react-intl'; + +const UploadProgress = React.createClass({ + + propTypes: { + active: React.PropTypes.bool, + progress: React.PropTypes.number + }, + + mixins: [PureRenderMixin], + + render () { + const { active, progress } = this.props; + + if (!active) { + return null; + } + + return ( +
+
+ +
+ +
+ + +
+ + {({ width }) => +
+ } + +
+
+
+ ); + } + +}); + +export default UploadProgress; diff --git a/app/assets/javascripts/components/features/compose/containers/compose_form_container.jsx b/app/assets/javascripts/components/features/compose/containers/compose_form_container.jsx index a67adbdd6..835b37516 100644 --- a/app/assets/javascripts/components/features/compose/containers/compose_form_container.jsx +++ b/app/assets/javascripts/components/features/compose/containers/compose_form_container.jsx @@ -30,7 +30,6 @@ const mapStateToProps = (state, props) => { spoiler_text: state.getIn(['compose', 'spoiler_text']), unlisted: state.getIn(['compose', 'unlisted'], ), private: state.getIn(['compose', 'private']), - fileDropDate: state.getIn(['compose', 'fileDropDate']), focusDate: state.getIn(['compose', 'focusDate']), preselectDate: state.getIn(['compose', 'preselectDate']), is_submitting: state.getIn(['compose', 'is_submitting']), diff --git a/app/assets/javascripts/components/features/compose/containers/upload_progress_container.jsx b/app/assets/javascripts/components/features/compose/containers/upload_progress_container.jsx new file mode 100644 index 000000000..b0f1d4d19 --- /dev/null +++ b/app/assets/javascripts/components/features/compose/containers/upload_progress_container.jsx @@ -0,0 +1,9 @@ +import { connect } from 'react-redux'; +import UploadProgress from '../components/upload_progress'; + +const mapStateToProps = (state, props) => ({ + active: state.getIn(['compose', 'is_uploading']), + progress: state.getIn(['compose', 'progress']) +}); + +export default connect(mapStateToProps)(UploadProgress); diff --git a/app/assets/javascripts/components/features/ui/components/upload_area.jsx b/app/assets/javascripts/components/features/ui/components/upload_area.jsx new file mode 100644 index 000000000..70b687019 --- /dev/null +++ b/app/assets/javascripts/components/features/ui/components/upload_area.jsx @@ -0,0 +1,32 @@ +import PureRenderMixin from 'react-addons-pure-render-mixin'; +import { Motion, spring } from 'react-motion'; +import { FormattedMessage } from 'react-intl'; + +const UploadArea = React.createClass({ + + propTypes: { + active: React.PropTypes.bool + }, + + mixins: [PureRenderMixin], + + render () { + const { active } = this.props; + + return ( + + {({ backgroundOpacity, backgroundScale }) => +
+
+
+
+
+
+ } + + ); + } + +}); + +export default UploadArea; diff --git a/app/assets/javascripts/components/features/ui/index.jsx b/app/assets/javascripts/components/features/ui/index.jsx index 900d83dba..10e989b2a 100644 --- a/app/assets/javascripts/components/features/ui/index.jsx +++ b/app/assets/javascripts/components/features/ui/index.jsx @@ -13,6 +13,7 @@ import { debounce } from 'react-decoration'; import { uploadCompose } from '../../actions/compose'; import { refreshTimeline } from '../../actions/timelines'; import { refreshNotifications } from '../../actions/notifications'; +import UploadArea from './components/upload_area'; const UI = React.createClass({ @@ -23,7 +24,8 @@ const UI = React.createClass({ getInitialState () { return { - width: window.innerWidth + width: window.innerWidth, + draggingOver: false }; }, @@ -41,7 +43,7 @@ const UI = React.createClass({ e.dataTransfer.dropEffect = 'copy'; if (e.dataTransfer.effectAllowed === 'all' || e.dataTransfer.effectAllowed === 'uninitialized') { - // + this.setState({ draggingOver: true }); } }, @@ -49,10 +51,15 @@ const UI = React.createClass({ e.preventDefault(); if (e.dataTransfer && e.dataTransfer.files.length === 1) { + this.setState({ draggingOver: false }); this.props.dispatch(uploadCompose(e.dataTransfer.files)); } }, + handleDragLeave () { + this.setState({ draggingOver: false }); + }, + componentWillMount () { window.addEventListener('resize', this.handleResize, { passive: true }); window.addEventListener('dragover', this.handleDragOver); @@ -69,12 +76,15 @@ const UI = React.createClass({ }, render () { + const { width, draggingOver } = this.state; + const { children } = this.props; + let mountedColumns; - if (isMobile(this.state.width)) { + if (isMobile(width)) { mountedColumns = ( - {this.props.children} + {children} ); } else { @@ -83,13 +93,13 @@ const UI = React.createClass({ - {this.props.children} + {children} ); } return ( -
+
{mountedColumns} @@ -97,6 +107,7 @@ const UI = React.createClass({ +
); } -- cgit