feat: sort images in a post

This commit is contained in:
Nevo David 2025-06-28 19:57:14 +07:00
parent 2b1020c528
commit 67eb6430e0
4 changed files with 73 additions and 44 deletions

View File

@ -3,6 +3,7 @@
import {
ClipboardEvent,
FC,
Fragment,
useCallback,
useEffect,
useMemo,
@ -19,7 +20,6 @@ import EventEmitter from 'events';
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import clsx from 'clsx';
import { VideoFrame } from '@gitroom/react/helpers/video.frame';
import { LoadingComponent } from '@gitroom/frontend/components/layout/loading';
import { MultipartFileUploader } from '@gitroom/frontend/components/media/new.uploader';
import dynamic from 'next/dynamic';
import { useUser } from '@gitroom/frontend/components/layout/user.context';
@ -29,6 +29,7 @@ import { DropFiles } from '@gitroom/frontend/components/layout/drop.files';
import { deleteDialog } from '@gitroom/react/helpers/delete.dialog';
import { useT } from '@gitroom/react/translation/get.transation.service.client';
import { ThirdPartyMedia } from '@gitroom/frontend/components/third-parties/third-party.media';
import { ReactSortable } from 'react-sortablejs';
const Polonto = dynamic(
() => import('@gitroom/frontend/components/launches/polonto')
);
@ -204,18 +205,21 @@ export const MediaBox: FC<{
}, [selectedMedia]);
const { data, mutate } = useSWR(`get-media-${page}`, loadMedia);
const finishUpload = useCallback(async (res: any) => {
const lastMedia = mediaList?.[0]?.id;
const newData = await mutate();
const untilLastMedia = newData.results.findIndex(
(f: any) => f.id === lastMedia
);
const onlyNewMedia = newData.results.slice(
0,
untilLastMedia === -1 ? newData.results.length : untilLastMedia
);
addNewMedia(onlyNewMedia)();
}, [mutate, addNewMedia, mediaList, selectedMedia]);
const finishUpload = useCallback(
async (res: any) => {
const lastMedia = mediaList?.[0]?.id;
const newData = await mutate();
const untilLastMedia = newData.results.findIndex(
(f: any) => f.id === lastMedia
);
const onlyNewMedia = newData.results.slice(
0,
untilLastMedia === -1 ? newData.results.length : untilLastMedia
);
addNewMedia(onlyNewMedia)();
},
[mutate, addNewMedia, mediaList, selectedMedia]
);
const dragAndDrop = useCallback(
async (event: ClipboardEvent<HTMLDivElement> | File[]) => {
@ -480,7 +484,8 @@ export const MultiMediaComponent: FC<{
};
}) => void;
}> = (props) => {
const { onOpen, onClose, name, error, text, onChange, value, allData } = props;
const { onOpen, onClose, name, error, text, onChange, value, allData } =
props;
const user = useUser();
useEffect(() => {
if (value) {
@ -614,32 +619,50 @@ export const MultiMediaComponent: FC<{
)}
</div>
{!!currentMedia &&
currentMedia.map((media, index) => (
<>
<div className="cursor-pointer w-[40px] h-[40px] border-2 border-tableBorder relative flex">
<div
className="w-full h-full"
onClick={() => window.open(mediaDirectory.set(media?.path))}
>
{media?.path?.indexOf('mp4') > -1 ? (
<VideoFrame url={mediaDirectory.set(media?.path)} />
) : (
<img
className="w-full h-full object-cover"
src={mediaDirectory.set(media?.path)}
/>
)}
{!!currentMedia && (
<ReactSortable
list={currentMedia}
setList={(value) =>
onChange({ target: { name: 'upload', value } })
}
className="flex gap-[10px] sortable-container"
animation={200}
swap={true}
handle=".dragging"
>
{currentMedia.map((media, index) => (
<Fragment key={media.id}>
<div className="cursor-pointer w-[40px] h-[40px] border-2 border-tableBorder relative flex transition-all">
<div className="dragging text-sm absolute pe-[1px] z-[10] pb-[3px] -start-[4px] -top-[4px] bg-blue-700 cursor-move rounded-full w-[15px] h-[15px] text-white flex justify-center items-center">
::
</div>
<div
className="w-full h-full"
onClick={() =>
window.open(mediaDirectory.set(media?.path))
}
>
{media?.path?.indexOf('mp4') > -1 ? (
<VideoFrame url={mediaDirectory.set(media?.path)} />
) : (
<img
className="w-full h-full object-cover"
src={mediaDirectory.set(media?.path)}
/>
)}
</div>
<div
onClick={clearMedia(index)}
className="rounded-full w-[15px] h-[15px] bg-red-800 text-white flex justify-center items-center absolute -end-[4px] -top-[4px]"
>
x
</div>
</div>
<div
onClick={clearMedia(index)}
className="rounded-full w-[15px] h-[15px] bg-red-800 text-textColor flex justify-center items-center absolute -end-[4px] -top-[4px]"
>
x
</div>
</div>
</>
))}
</Fragment>
))}
</ReactSortable>
)}
</div>
</div>
<div className="text-[12px] text-red-400">{error}</div>

View File

@ -59,6 +59,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => {
setTags,
integrations,
setSelectedIntegrations,
locked,
} = useLaunchStore(
useShallow((state) => ({
hide: state.hide,
@ -71,6 +72,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => {
selectedIntegrations: state.selectedIntegrations,
integrations: state.integrations,
setSelectedIntegrations: state.setSelectedIntegrations,
locked: state.locked,
}))
);
@ -316,7 +318,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => {
onClick={deletePost}
className="rounded-[4px] border-2 border-red-400 text-red-400"
secondary={true}
disabled={loading}
disabled={loading || locked}
>
{t('delete_post', 'Delete Post')}
</Button>
@ -327,7 +329,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => {
onClick={schedule('draft')}
className="rounded-[4px] border-2 border-customColor21"
secondary={true}
disabled={selectedIntegrations.length === 0 || loading}
disabled={selectedIntegrations.length === 0 || loading || locked}
>
{t('save_as_draft', 'Save as draft')}
</Button>
@ -336,7 +338,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => {
{addEditSets && (
<Button
className="rounded-[4px] relative group"
disabled={selectedIntegrations.length === 0 || loading}
disabled={selectedIntegrations.length === 0 || loading || locked}
onClick={schedule('draft')}
>
Save Set
@ -345,7 +347,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => {
{!addEditSets && (
<Button
className="rounded-[4px] relative group"
disabled={selectedIntegrations.length === 0 || loading}
disabled={selectedIntegrations.length === 0 || loading || locked}
>
<div className="flex justify-center items-center gap-[5px] h-full">
<div
@ -378,7 +380,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => {
onClick={schedule('now')}
className={clsx(
'hidden group-hover:flex hover:flex flex-col justify-center absolute start-0 top-[100%] w-full h-[40px] bg-customColor22 border border-tableBorder',
loading &&
(locked || loading) &&
'cursor-not-allowed pointer-events-none opacity-50'
)}
>

View File

@ -181,6 +181,7 @@
"react-hook-form": "^7.58.1",
"react-i18next": "^15.5.2",
"react-loading": "^2.0.3",
"react-sortablejs": "^6.1.4",
"react-tag-autocomplete": "^7.2.0",
"react-tooltip": "^5.26.2",
"react-use-cookie": "^1.6.1",

View File

@ -420,6 +420,9 @@ importers:
react-loading:
specifier: ^2.0.3
version: 2.0.3(prop-types@15.8.1)(react@18.3.1)
react-sortablejs:
specifier: ^6.1.4
version: 6.1.4(@types/sortablejs@1.15.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sortablejs@1.15.6)
react-tag-autocomplete:
specifier: ^7.2.0
version: 7.5.0(react@18.3.1)