Merge branch 'feature-media-reordering' into 'main'
Add ability to reorder uploaded media before posting in web UI See merge request soapbox-pub/soapbox!2984
This commit is contained in:
commit
93e9760274
5 changed files with 84 additions and 7 deletions
|
@ -89,6 +89,8 @@ const COMPOSE_SET_STATUS = 'COMPOSE_SET_STATUS' as const;
|
|||
|
||||
const COMPOSE_EDITOR_STATE_SET = 'COMPOSE_EDITOR_STATE_SET' as const;
|
||||
|
||||
const COMPOSE_CHANGE_MEDIA_ORDER = 'COMPOSE_CHANGE_MEDIA_ORDER' as const;
|
||||
|
||||
const messages = defineMessages({
|
||||
scheduleError: { id: 'compose.invalid_schedule', defaultMessage: 'You must schedule a post at least 5 minutes out.' },
|
||||
success: { id: 'compose.submit_success', defaultMessage: 'Your post was sent!' },
|
||||
|
@ -851,6 +853,13 @@ const setEditorState = (composeId: string, editorState: EditorState | string | n
|
|||
editorState: editorState,
|
||||
});
|
||||
|
||||
const changeMediaOrder = (composeId: string, a: string, b: string) => ({
|
||||
type: COMPOSE_CHANGE_MEDIA_ORDER,
|
||||
id: composeId,
|
||||
a,
|
||||
b,
|
||||
});
|
||||
|
||||
type ComposeAction =
|
||||
ComposeSetStatusAction
|
||||
| ReturnType<typeof changeCompose>
|
||||
|
@ -897,6 +906,7 @@ type ComposeAction =
|
|||
| ComposeRemoveFromMentionsAction
|
||||
| ComposeEventReplyAction
|
||||
| ReturnType<typeof setEditorState>
|
||||
| ReturnType<typeof changeMediaOrder>
|
||||
|
||||
export {
|
||||
COMPOSE_CHANGE,
|
||||
|
@ -945,6 +955,7 @@ export {
|
|||
COMPOSE_SET_STATUS,
|
||||
COMPOSE_EDITOR_STATE_SET,
|
||||
COMPOSE_SET_GROUP_TIMELINE_VISIBLE,
|
||||
COMPOSE_CHANGE_MEDIA_ORDER,
|
||||
setComposeToStatus,
|
||||
changeCompose,
|
||||
replyCompose,
|
||||
|
@ -1000,5 +1011,6 @@ export {
|
|||
removeFromMentions,
|
||||
eventDiscussionCompose,
|
||||
setEditorState,
|
||||
changeMediaOrder,
|
||||
type ComposeAction,
|
||||
};
|
||||
|
|
|
@ -61,7 +61,7 @@ const messages = defineMessages({
|
|||
descriptionMissingTitle: { id: 'upload_form.description_missing.title', defaultMessage: 'This attachment doesn\'t have a description' },
|
||||
});
|
||||
|
||||
interface IUpload {
|
||||
interface IUpload extends Pick<React.HTMLAttributes<HTMLDivElement>, 'onDragStart' | 'onDragEnter' | 'onDragEnd'> {
|
||||
media: Attachment;
|
||||
onSubmit?(): void;
|
||||
onDelete?(): void;
|
||||
|
@ -75,6 +75,9 @@ const Upload: React.FC<IUpload> = ({
|
|||
onSubmit,
|
||||
onDelete,
|
||||
onDescriptionChange,
|
||||
onDragStart,
|
||||
onDragEnter,
|
||||
onDragEnd,
|
||||
descriptionLimit,
|
||||
withPreview = true,
|
||||
}) => {
|
||||
|
@ -151,7 +154,18 @@ const Upload: React.FC<IUpload> = ({
|
|||
);
|
||||
|
||||
return (
|
||||
<div className='compose-form__upload' tabIndex={0} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} onClick={handleClick} role='button'>
|
||||
<div
|
||||
className='compose-form__upload'
|
||||
tabIndex={0}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
onClick={handleClick}
|
||||
role='button'
|
||||
draggable
|
||||
onDragStart={onDragStart}
|
||||
onDragEnter={onDragEnter}
|
||||
onDragEnd={onDragEnd}
|
||||
>
|
||||
<Blurhash hash={media.blurhash} className='media-gallery__preview' />
|
||||
<Motion defaultStyle={{ scale: 0.8 }} style={{ scale: spring(1, { stiffness: 180, damping: 12 }) }}>
|
||||
{({ scale }) => (
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
|
||||
import { changeMediaOrder } from 'soapbox/actions/compose';
|
||||
import { HStack } from 'soapbox/components/ui';
|
||||
import { useCompose } from 'soapbox/hooks';
|
||||
import { useAppDispatch, useCompose } from 'soapbox/hooks';
|
||||
|
||||
import Upload from './upload';
|
||||
import UploadProgress from './upload-progress';
|
||||
|
@ -15,15 +16,42 @@ interface IUploadForm {
|
|||
}
|
||||
|
||||
const UploadForm: React.FC<IUploadForm> = ({ composeId, onSubmit }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const mediaIds = useCompose(composeId).media_attachments.map((item: AttachmentEntity) => item.id);
|
||||
|
||||
const dragItem = useRef<string | null>();
|
||||
const dragOverItem = useRef<string | null>();
|
||||
|
||||
const handleDragStart = useCallback((id: string) => {
|
||||
dragItem.current = id;
|
||||
}, [dragItem]);
|
||||
|
||||
const handleDragEnter = useCallback((id: string) => {
|
||||
dragOverItem.current = id;
|
||||
}, [dragOverItem]);
|
||||
|
||||
const handleDragEnd = useCallback(() => {
|
||||
dispatch(changeMediaOrder(composeId, dragItem.current!, dragOverItem.current!));
|
||||
dragItem.current = null;
|
||||
dragOverItem.current = null;
|
||||
}, [dragItem, dragOverItem]);
|
||||
|
||||
return (
|
||||
<div className='overflow-hidden'>
|
||||
<UploadProgress composeId={composeId} />
|
||||
|
||||
<HStack wrap className={clsx('overflow-hidden', mediaIds.size !== 0 && 'p-1')}>
|
||||
{mediaIds.map((id: string) => (
|
||||
<Upload id={id} key={id} composeId={composeId} onSubmit={onSubmit} />
|
||||
<Upload
|
||||
id={id}
|
||||
key={id}
|
||||
composeId={composeId}
|
||||
onSubmit={onSubmit}
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnter={handleDragEnter}
|
||||
onDragEnd={handleDragEnd}
|
||||
/>
|
||||
))}
|
||||
</HStack>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
import { undoUploadCompose, changeUploadCompose } from 'soapbox/actions/compose';
|
||||
import Upload from 'soapbox/components/upload';
|
||||
|
@ -8,9 +8,12 @@ interface IUploadCompose {
|
|||
id: string;
|
||||
composeId: string;
|
||||
onSubmit?(): void;
|
||||
onDragStart: (id: string) => void;
|
||||
onDragEnter: (id: string) => void;
|
||||
onDragEnd: () => void;
|
||||
}
|
||||
|
||||
const UploadCompose: React.FC<IUploadCompose> = ({ composeId, id, onSubmit }) => {
|
||||
const UploadCompose: React.FC<IUploadCompose> = ({ composeId, id, onSubmit, onDragStart, onDragEnter, onDragEnd }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { pleroma: { metadata: { description_limit: descriptionLimit } } } = useInstance();
|
||||
|
||||
|
@ -24,12 +27,23 @@ const UploadCompose: React.FC<IUploadCompose> = ({ composeId, id, onSubmit }) =>
|
|||
dispatch(undoUploadCompose(composeId, media.id));
|
||||
};
|
||||
|
||||
const handleDragStart = useCallback(() => {
|
||||
onDragStart(id);
|
||||
}, [onDragStart, id]);
|
||||
|
||||
const handleDragEnter = useCallback(() => {
|
||||
onDragEnter(id);
|
||||
}, [onDragEnter, id]);
|
||||
|
||||
return (
|
||||
<Upload
|
||||
media={media}
|
||||
onDelete={handleDelete}
|
||||
onDescriptionChange={handleDescriptionChange}
|
||||
onSubmit={onSubmit}
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnter={handleDragEnter}
|
||||
onDragEnd={onDragEnd}
|
||||
descriptionLimit={descriptionLimit}
|
||||
withPreview
|
||||
/>
|
||||
|
|
|
@ -54,6 +54,7 @@ import {
|
|||
COMPOSE_EDITOR_STATE_SET,
|
||||
COMPOSE_SET_GROUP_TIMELINE_VISIBLE,
|
||||
ComposeAction,
|
||||
COMPOSE_CHANGE_MEDIA_ORDER,
|
||||
} from '../actions/compose';
|
||||
import { EVENT_COMPOSE_CANCEL, EVENT_FORM_SET, type EventsAction } from '../actions/events';
|
||||
import { ME_FETCH_SUCCESS, ME_PATCH_SUCCESS, MeAction } from '../actions/me';
|
||||
|
@ -520,6 +521,14 @@ export default function compose(state = initialState, action: ComposeAction | Ev
|
|||
return updateCompose(state, 'event-compose-modal', compose => compose.set('text', ''));
|
||||
case EVENT_FORM_SET:
|
||||
return updateCompose(state, 'event-compose-modal', compose => compose.set('text', action.text));
|
||||
case COMPOSE_CHANGE_MEDIA_ORDER:
|
||||
return updateCompose(state, action.id, compose => compose.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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue