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}
/>
- {isUploading || media && media.size ? (
-
- ) : null}
+
{
+ 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 (
+
+
+ {({ scale }) => (
+
+
+
+ {media.get('type') === 'image' && }
+
+
+
+
+
+
+ )}
+
+
+ );
+ }
+
+}
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 (
+
+
+
+
+ {mediaIds.map(id => (
+
+ ))}
+
+
+ );
+ }
+
+}
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 (
+
+
+
+
+
+
+
+
+ {({ width }) =>
+ ()
+ }
+
+
+
+
+ );
+ }
+
+}
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);
diff --git a/app/javascript/flavours/glitch/features/composer/upload_form/index.js b/app/javascript/flavours/glitch/features/composer/upload_form/index.js
deleted file mode 100644
index c2ff66623..000000000
--- a/app/javascript/flavours/glitch/features/composer/upload_form/index.js
+++ /dev/null
@@ -1,60 +0,0 @@
-// Package imports.
-import classNames from 'classnames';
-import PropTypes from 'prop-types';
-import React from 'react';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-
-// Components.
-import ComposerUploadFormItem from './item';
-import ComposerUploadFormProgress from './progress';
-
-// The component.
-export default function ComposerUploadForm ({
- intl,
- media,
- onChangeDescription,
- onOpenFocalPointModal,
- onRemove,
- progress,
- uploading,
- handleRef,
-}) {
- const computedClass = classNames('composer--upload_form', { uploading });
-
- // The result.
- return (
-
- {uploading ?
: null}
- {media ? (
-
- {media.map(item => (
-
- ))}
-
- ) : null}
-
- );
-}
-
-// Props.
-ComposerUploadForm.propTypes = {
- intl: PropTypes.object.isRequired,
- media: ImmutablePropTypes.list,
- onChangeDescription: PropTypes.func.isRequired,
- onRemove: PropTypes.func.isRequired,
- progress: PropTypes.number,
- uploading: PropTypes.bool,
- handleRef: PropTypes.func,
-};
diff --git a/app/javascript/flavours/glitch/features/composer/upload_form/item/index.js b/app/javascript/flavours/glitch/features/composer/upload_form/item/index.js
deleted file mode 100644
index 4f5f66f04..000000000
--- a/app/javascript/flavours/glitch/features/composer/upload_form/item/index.js
+++ /dev/null
@@ -1,202 +0,0 @@
-// Package imports.
-import classNames from 'classnames';
-import PropTypes from 'prop-types';
-import React from 'react';
-import {
- FormattedMessage,
- defineMessages,
-} from 'react-intl';
-import spring from 'react-motion/lib/spring';
-
-// Components.
-import IconButton from 'flavours/glitch/components/icon_button';
-
-// Utils.
-import Motion from 'flavours/glitch/util/optional_motion';
-import { assignHandlers } from 'flavours/glitch/util/react_helpers';
-import { isUserTouching } from 'flavours/glitch/util/is_mobile';
-
-// Messages.
-const messages = defineMessages({
- undo: {
- defaultMessage: 'Undo',
- id: 'upload_form.undo',
- },
- description: {
- defaultMessage: 'Describe for the visually impaired',
- id: 'upload_form.description',
- },
- crop: {
- defaultMessage: 'Crop',
- id: 'upload_form.focus',
- },
-});
-
-// Handlers.
-const handlers = {
-
- // On blur, we save the description for the media item.
- handleBlur () {
- const {
- id,
- onChangeDescription,
- } = this.props;
- const { dirtyDescription } = this.state;
-
- this.setState({ dirtyDescription: null, focused: false });
-
- if (id && onChangeDescription && dirtyDescription !== null) {
- onChangeDescription(id, dirtyDescription);
- }
- },
-
- // When the value of our description changes, we store it in the
- // temp value `dirtyDescription` in our state.
- handleChange ({ target: { value } }) {
- this.setState({ dirtyDescription: value });
- },
-
- // Records focus on the media item.
- handleFocus () {
- this.setState({ focused: true });
- },
-
- // Records the start of a hover over the media item.
- handleMouseEnter () {
- this.setState({ hovered: true });
- },
-
- // Records the end of a hover over the media item.
- handleMouseLeave () {
- this.setState({ hovered: false });
- },
-
- // Removes the media item.
- handleRemove () {
- const {
- id,
- onRemove,
- } = this.props;
- if (id && onRemove) {
- onRemove(id);
- }
- },
-
- // Opens the focal point modal.
- handleFocalPointClick () {
- const {
- id,
- onOpenFocalPointModal,
- } = this.props;
- if (id && onOpenFocalPointModal) {
- onOpenFocalPointModal(id);
- }
- },
-};
-
-// The component.
-export default class ComposerUploadFormItem extends React.PureComponent {
-
- // Constructor.
- constructor (props) {
- super(props);
- assignHandlers(this, handlers);
- this.state = {
- hovered: false,
- focused: false,
- dirtyDescription: null,
- };
- }
-
- // Rendering.
- render () {
- const {
- handleBlur,
- handleChange,
- handleFocus,
- handleMouseEnter,
- handleMouseLeave,
- handleRemove,
- handleFocalPointClick,
- } = this.handlers;
- const {
- intl,
- preview,
- focusX,
- focusY,
- mediaType,
- } = this.props;
- const {
- focused,
- hovered,
- dirtyDescription,
- } = this.state;
- const active = hovered || focused || isUserTouching();
- const computedClass = classNames('composer--upload_form--item', { active });
- const x = ((focusX / 2) + .5) * 100;
- const y = ((focusY / -2) + .5) * 100;
- const description = dirtyDescription || (dirtyDescription !== '' && this.props.description) || '';
-
- // The result.
- return (
-
-
- {({ scale }) => (
-
-
-
- {mediaType === 'image' && }
-
-
-
- )}
-
-
- );
- }
-
-}
-
-// Props.
-ComposerUploadFormItem.propTypes = {
- description: PropTypes.string,
- id: PropTypes.string,
- intl: PropTypes.object.isRequired,
- onChangeDescription: PropTypes.func.isRequired,
- onOpenFocalPointModal: PropTypes.func.isRequired,
- onRemove: PropTypes.func.isRequired,
- focusX: PropTypes.number,
- focusY: PropTypes.number,
- mediaType: PropTypes.string,
- preview: PropTypes.string,
-};
diff --git a/app/javascript/flavours/glitch/features/composer/upload_form/progress/index.js b/app/javascript/flavours/glitch/features/composer/upload_form/progress/index.js
deleted file mode 100644
index 8c4b0eea6..000000000
--- a/app/javascript/flavours/glitch/features/composer/upload_form/progress/index.js
+++ /dev/null
@@ -1,52 +0,0 @@
-// Package imports.
-import PropTypes from 'prop-types';
-import React from 'react';
-import {
- defineMessages,
- FormattedMessage,
-} from 'react-intl';
-import spring from 'react-motion/lib/spring';
-
-// Components.
-import Icon from 'flavours/glitch/components/icon';
-
-// Utils.
-import Motion from 'flavours/glitch/util/optional_motion';
-
-// Messages.
-const messages = defineMessages({
- upload: {
- defaultMessage: 'Uploading...',
- id: 'upload_progress.label',
- },
-});
-
-// The component.
-export default function ComposerUploadFormProgress ({ progress }) {
-
- // The result.
- return (
-
-
-
-
-
-
- {({ width }) =>
- ()
- }
-
-
-
-
- );
-}
-
-// Props.
-ComposerUploadFormProgress.propTypes = { progress: PropTypes.number };