diff --git a/app/javascript/flavours/glitch/actions/compose.js b/app/javascript/flavours/glitch/actions/compose.js
index 2646aa7b02..1dca94ae68 100644
--- a/app/javascript/flavours/glitch/actions/compose.js
+++ b/app/javascript/flavours/glitch/actions/compose.js
@@ -81,6 +81,7 @@ export const INIT_MEDIA_EDIT_MODAL = 'INIT_MEDIA_EDIT_MODAL';
export const COMPOSE_CHANGE_MEDIA_DESCRIPTION = 'COMPOSE_CHANGE_MEDIA_DESCRIPTION';
export const COMPOSE_CHANGE_MEDIA_FOCUS = 'COMPOSE_CHANGE_MEDIA_FOCUS';
+export const COMPOSE_CHANGE_MEDIA_ORDER = 'COMPOSE_CHANGE_MEDIA_ORDER';
export const COMPOSE_SET_STATUS = 'COMPOSE_SET_STATUS';
export const COMPOSE_FOCUS = 'COMPOSE_FOCUS';
@@ -845,3 +846,9 @@ export function changePollSettings(expiresIn, isMultiple) {
isMultiple,
};
}
+
+export const changeMediaOrder = (a, b) => ({
+ type: COMPOSE_CHANGE_MEDIA_ORDER,
+ a,
+ b,
+});
diff --git a/app/javascript/flavours/glitch/features/compose/components/compose_form.jsx b/app/javascript/flavours/glitch/features/compose/components/compose_form.jsx
index a09830d2c6..6f16abfc7f 100644
--- a/app/javascript/flavours/glitch/features/compose/components/compose_form.jsx
+++ b/app/javascript/flavours/glitch/features/compose/components/compose_form.jsx
@@ -21,7 +21,6 @@ import PollButtonContainer from '../containers/poll_button_container';
import PrivacyDropdownContainer from '../containers/privacy_dropdown_container';
import SpoilerButtonContainer from '../containers/spoiler_button_container';
import UploadButtonContainer from '../containers/upload_button_container';
-import UploadFormContainer from '../containers/upload_form_container';
import WarningContainer from '../containers/warning_container';
import { countableText } from '../util/counter';
@@ -34,6 +33,7 @@ import { PollForm } from "./poll_form";
import { ReplyIndicator } from './reply_indicator';
import { SecondaryPrivacyButton } from './secondary_privacy_button';
import { ThreadModeButton } from './thread_mode_button';
+import { UploadForm } from './upload_form';
const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
@@ -308,7 +308,7 @@ class ComposeForm extends ImmutablePureComponent {
/>
-
+
diff --git a/app/javascript/flavours/glitch/features/compose/components/upload.jsx b/app/javascript/flavours/glitch/features/compose/components/upload.jsx
index a6115184e3..790d76264d 100644
--- a/app/javascript/flavours/glitch/features/compose/components/upload.jsx
+++ b/app/javascript/flavours/glitch/features/compose/components/upload.jsx
@@ -1,77 +1,81 @@
import PropTypes from 'prop-types';
+import { useCallback } from 'react';
import { FormattedMessage } from 'react-intl';
import classNames from 'classnames';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
+import { useDispatch, useSelector } from 'react-redux';
import spring from 'react-motion/lib/spring';
import CloseIcon from '@/material-icons/400-20px/close.svg?react';
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
import WarningIcon from '@/material-icons/400-24px/warning.svg?react';
+import { undoUploadCompose, initMediaEditModal } from 'flavours/glitch/actions/compose';
import { Blurhash } from 'flavours/glitch/components/blurhash';
import { Icon } from 'flavours/glitch/components/icon';
+import Motion from 'flavours/glitch/features/ui/util/optional_motion';
-import Motion from '../../ui/util/optional_motion';
+export const Upload = ({ id, onDragStart, onDragEnter, onDragEnd }) => {
+ const dispatch = useDispatch();
+ const media = useSelector(state => state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id));
+ const sensitive = useSelector(state => state.getIn(['compose', 'sensitive']));
-export default class Upload extends ImmutablePureComponent {
+ const handleUndoClick = useCallback(() => {
+ dispatch(undoUploadCompose(id));
+ }, [dispatch, id]);
- static propTypes = {
- media: ImmutablePropTypes.map.isRequired,
- sensitive: PropTypes.bool,
- onUndo: PropTypes.func.isRequired,
- onOpenFocalPoint: PropTypes.func.isRequired,
- };
+ const handleFocalPointClick = useCallback(() => {
+ dispatch(initMediaEditModal(id));
+ }, [dispatch, id]);
- handleUndoClick = e => {
- e.stopPropagation();
- this.props.onUndo(this.props.media.get('id'));
- };
+ const handleDragStart = useCallback(() => {
+ onDragStart(id);
+ }, [onDragStart, id]);
- handleFocalPointClick = e => {
- e.stopPropagation();
- this.props.onOpenFocalPoint(this.props.media.get('id'));
- };
+ const handleDragEnter = useCallback(() => {
+ onDragEnter(id);
+ }, [onDragEnter, id]);
- render () {
- const { media, sensitive } = this.props;
-
- if (!media) {
- return null;
- }
-
- 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;
- const missingDescription = (media.get('description') || '').length === 0;
-
- return (
-
-
- {({ scale }) => (
-
- {sensitive &&
}
-
-
-
-
-
-
-
-
-
-
- )}
-
-
- );
+ if (!media) {
+ return null;
}
-}
+ 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;
+ const missingDescription = (media.get('description') || '').length === 0;
+
+ return (
+
+
+ {({ scale }) => (
+
+ {sensitive &&
}
+
+
+
+
+
+
+
+
+
+
+ )}
+
+
+ );
+};
+
+Upload.propTypes = {
+ id: PropTypes.string,
+ onDragEnter: PropTypes.func,
+ onDragStart: PropTypes.func,
+ onDragEnd: PropTypes.func,
+};
diff --git a/app/javascript/flavours/glitch/features/compose/components/upload_form.jsx b/app/javascript/flavours/glitch/features/compose/components/upload_form.jsx
index 5c6406c3ba..2b26735f5e 100644
--- a/app/javascript/flavours/glitch/features/compose/components/upload_form.jsx
+++ b/app/javascript/flavours/glitch/features/compose/components/upload_form.jsx
@@ -1,35 +1,56 @@
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import ImmutablePureComponent from 'react-immutable-pure-component';
+import { useRef, useCallback } from 'react';
-import UploadContainer from '../containers/upload_container';
-import UploadProgressContainer from '../containers/upload_progress_container';
+import { useSelector, useDispatch } from 'react-redux';
+
+import { changeMediaOrder } from 'flavours/glitch/actions/compose';
import { SensitiveButton } from './sensitive_button';
+import { Upload } from './upload';
+import { UploadProgress } from './upload_progress';
-export default class UploadForm extends ImmutablePureComponent {
+export const UploadForm = () => {
+ const dispatch = useDispatch();
+ const mediaIds = useSelector(state => state.getIn(['compose', 'media_attachments']).map(item => item.get('id')));
+ const active = useSelector(state => state.getIn(['compose', 'is_uploading']));
+ const progress = useSelector(state => state.getIn(['compose', 'progress']));
+ const isProcessing = useSelector(state => state.getIn(['compose', 'is_processing']));
- static propTypes = {
- mediaIds: ImmutablePropTypes.list.isRequired,
- };
+ const dragItem = useRef();
+ const dragOverItem = useRef();
- render () {
- const { mediaIds } = this.props;
+ const handleDragStart = useCallback(id => {
+ dragItem.current = id;
+ }, [dragItem]);
- return (
- <>
-
+ const handleDragEnter = useCallback(id => {
+ dragOverItem.current = id;
+ }, [dragOverItem]);
- {mediaIds.size > 0 && (
-
- {mediaIds.map(id => (
-
- ))}
-
- )}
+ const handleDragEnd = useCallback(() => {
+ dispatch(changeMediaOrder(dragItem.current, dragOverItem.current));
+ dragItem.current = null;
+ dragOverItem.current = null;
+ }, [dispatch, dragItem, dragOverItem]);
- {!mediaIds.isEmpty() &&
}
- >
- );
- }
+ return (
+ <>
+
-}
+ {mediaIds.size > 0 && (
+
+ {mediaIds.map(id => (
+
+ ))}
+
+ )}
+
+ {!mediaIds.isEmpty() &&
}
+ >
+ );
+};
diff --git a/app/javascript/flavours/glitch/features/compose/components/upload_progress.jsx b/app/javascript/flavours/glitch/features/compose/components/upload_progress.jsx
index 7af5d9090e..f72f2c4603 100644
--- a/app/javascript/flavours/glitch/features/compose/components/upload_progress.jsx
+++ b/app/javascript/flavours/glitch/features/compose/components/upload_progress.jsx
@@ -1,5 +1,4 @@
import PropTypes from 'prop-types';
-import { PureComponent } from 'react';
import { FormattedMessage } from 'react-intl';
@@ -10,46 +9,40 @@ import { Icon } from 'flavours/glitch/components/icon';
import Motion from '../../ui/util/optional_motion';
-export default class UploadProgress extends PureComponent {
-
- static propTypes = {
- active: PropTypes.bool,
- progress: PropTypes.number,
- isProcessing: PropTypes.bool,
- };
-
- render () {
- const { active, progress, isProcessing } = this.props;
-
- if (!active) {
- return null;
- }
-
- let message;
-
- if (isProcessing) {
- message =
;
- } else {
- message =
;
- }
-
- return (
-
-
-
-
- {message}
-
-
-
- {({ width }) =>
-
- }
-
-
-
-
- );
+export const UploadProgress = ({ active, progress, isProcessing }) => {
+ if (!active) {
+ return null;
}
-}
+ let message;
+
+ if (isProcessing) {
+ message =
;
+ } else {
+ message =
;
+ }
+
+ return (
+
+
+
+
+ {message}
+
+
+
+ {({ width }) =>
+
+ }
+
+
+
+
+ );
+};
+
+UploadProgress.propTypes = {
+ active: PropTypes.bool,
+ progress: PropTypes.number,
+ isProcessing: PropTypes.bool,
+};
diff --git a/app/javascript/flavours/glitch/features/compose/containers/upload_container.js b/app/javascript/flavours/glitch/features/compose/containers/upload_container.js
deleted file mode 100644
index 5afe6eaa1a..0000000000
--- a/app/javascript/flavours/glitch/features/compose/containers/upload_container.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import { connect } from 'react-redux';
-
-import { undoUploadCompose, initMediaEditModal, submitCompose } from '../../../actions/compose';
-import Upload from '../components/upload';
-
-const mapStateToProps = (state, { id }) => ({
- media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id),
- sensitive: state.getIn(['compose', 'sensitive']),
-});
-
-const mapDispatchToProps = dispatch => ({
-
- onUndo: id => {
- dispatch(undoUploadCompose(id));
- },
-
- onOpenFocalPoint: id => {
- dispatch(initMediaEditModal(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
deleted file mode 100644
index 336525cf53..0000000000
--- a/app/javascript/flavours/glitch/features/compose/containers/upload_form_container.js
+++ /dev/null
@@ -1,9 +0,0 @@
-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
deleted file mode 100644
index ffff321c3f..0000000000
--- a/app/javascript/flavours/glitch/features/compose/containers/upload_progress_container.js
+++ /dev/null
@@ -1,11 +0,0 @@
-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']),
- isProcessing: state.getIn(['compose', 'is_processing']),
-});
-
-export default connect(mapStateToProps)(UploadProgress);
diff --git a/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.jsx b/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.jsx
index bf31c137f1..0ee84dd0c2 100644
--- a/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.jsx
+++ b/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.jsx
@@ -22,7 +22,7 @@ import { GIFV } from 'flavours/glitch/components/gifv';
import { IconButton } from 'flavours/glitch/components/icon_button';
import Audio from 'flavours/glitch/features/audio';
import { CharacterCounter } from 'flavours/glitch/features/compose/components/character_counter';
-import UploadProgress from 'flavours/glitch/features/compose/components/upload_progress';
+import { UploadProgress } from 'flavours/glitch/features/compose/components/upload_progress';
import { Tesseract as fetchTesseract } from 'flavours/glitch/features/ui/util/async-components';
import { me } from 'flavours/glitch/initial_state';
import { assetHost } from 'flavours/glitch/utils/config';
diff --git a/app/javascript/flavours/glitch/reducers/compose.js b/app/javascript/flavours/glitch/reducers/compose.js
index f96d9e959f..36430e8238 100644
--- a/app/javascript/flavours/glitch/reducers/compose.js
+++ b/app/javascript/flavours/glitch/reducers/compose.js
@@ -48,6 +48,7 @@ import {
INIT_MEDIA_EDIT_MODAL,
COMPOSE_CHANGE_MEDIA_DESCRIPTION,
COMPOSE_CHANGE_MEDIA_FOCUS,
+ COMPOSE_CHANGE_MEDIA_ORDER,
COMPOSE_SET_STATUS,
COMPOSE_FOCUS,
} from '../actions/compose';
@@ -656,6 +657,14 @@ export default function compose(state = initialState, action) {
return state.set('language', action.language);
case COMPOSE_FOCUS:
return state.set('focusDate', new Date()).update('text', text => text.length > 0 ? text : action.defaultText);
+ case COMPOSE_CHANGE_MEDIA_ORDER:
+ return state.update('media_attachments', list => {
+ const indexA = list.findIndex(x => x.get('id') === action.a);
+ const moveItem = list.get(indexA);
+ const indexB = list.findIndex(x => x.get('id') === action.b);
+
+ return list.splice(indexA, 1).splice(indexB, 0, moveItem);
+ });
default:
return state;
}