Skip to content

Commit 5cb6966

Browse files
committed
feat: drag and drop files
1 parent baea8b2 commit 5cb6966

File tree

4 files changed

+184
-134
lines changed

4 files changed

+184
-134
lines changed

apps/frontend/src/components/layout/drop.files.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,24 @@ import { useDropzone } from 'react-dropzone';
22
import { FC, ReactNode } from 'react';
33
import { useT } from '@gitroom/react/translation/get.transation.service.client';
44
import clsx from 'clsx';
5+
import { useToaster } from '@gitroom/react/toaster/toaster';
56
export const DropFiles: FC<{
67
children: ReactNode;
78
className?: string;
89
onDrop: (files: File[]) => void;
10+
disabled?: boolean;
911
}> = (props) => {
1012
const t = useT();
13+
const toaster = useToaster();
1114

1215
const { getRootProps, isDragActive } = useDropzone({
13-
onDrop: props.onDrop,
16+
onDrop: (files) => {
17+
if (props.disabled) {
18+
toaster.show('Upload current in progress, please wait and then try again.', 'warning');
19+
return ;
20+
}
21+
props.onDrop(files);
22+
},
1423
});
1524
return (
1625
<div {...getRootProps()} className={clsx("relative", props.className)}>

apps/frontend/src/components/media/media.component.tsx

Lines changed: 69 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { Media } from '@prisma/client';
1818
import { useMediaDirectory } from '@gitroom/react/helpers/use.media.directory';
1919
import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values';
2020
import EventEmitter from 'events';
21+
import { useToaster } from '@gitroom/react/toaster/toaster';
2122
import clsx from 'clsx';
2223
import { VideoFrame } from '@gitroom/react/helpers/video.frame';
2324
import { useUppyUploader } from '@gitroom/frontend/components/media/new.uploader';
@@ -48,6 +49,7 @@ import {
4849
} from '@gitroom/frontend/components/ui/icons';
4950
import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store';
5051
import { useShallow } from 'zustand/react/shallow';
52+
import { LoadingComponent } from '@gitroom/frontend/components/layout/loading';
5153
const Polonto = dynamic(
5254
() => import('@gitroom/frontend/components/launches/polonto')
5355
);
@@ -194,6 +196,7 @@ export const showMediaBox = (
194196
showModalEmitter.emit('show-modal', callback);
195197
};
196198
const CHUNK_SIZE = 1024 * 1024;
199+
const MAX_UPLOAD_SIZE = 1024 * 1024 * 1024; // 1 GB
197200
export const MediaBox: FC<{
198201
setMedia: (params: { id: string; path: string }[]) => void;
199202
standalone?: boolean;
@@ -203,6 +206,7 @@ export const MediaBox: FC<{
203206
const [page, setPage] = useState(0);
204207
const fetch = useFetch();
205208
const modals = useModals();
209+
const toaster = useToaster();
206210
const loadMedia = useCallback(async () => {
207211
return (await fetch(`/media?page=${page + 1}`)).json();
208212
}, [page]);
@@ -211,6 +215,7 @@ export const MediaBox: FC<{
211215
const t = useT();
212216
const uploaderRef = useRef<any>(null);
213217
const mediaDirectory = useMediaDirectory();
218+
const [loading, setLoading] = useState(false);
214219

215220
const uppy = useUppyUploader({
216221
allowedFileTypes:
@@ -220,7 +225,6 @@ export const MediaBox: FC<{
220225
? 'video/mp4'
221226
: 'image/*,video/mp4',
222227
onUploadSuccess: async (arr) => {
223-
uppy.clear();
224228
await mutate();
225229
if (standalone) {
226230
return;
@@ -229,6 +233,8 @@ export const MediaBox: FC<{
229233
return [...prevSelected, ...arr];
230234
});
231235
},
236+
onStart: () => setLoading(true),
237+
onEnd: () => setLoading(false),
232238
});
233239

234240
const addRemoveSelected = useCallback(
@@ -255,11 +261,29 @@ export const MediaBox: FC<{
255261
modals.closeCurrent();
256262
}, [selected]);
257263

258-
const addToUpload = useCallback(async (e: ChangeEvent<HTMLInputElement>) => {
259-
const files = Array.from(e.target.files).slice(0, 5);
260-
// @ts-ignore
261-
uppy.addFiles(files);
262-
}, []);
264+
const addToUpload = useCallback(
265+
async (e: ChangeEvent<HTMLInputElement>) => {
266+
const files = Array.from(e.target.files || []);
267+
const totalSize = files.reduce((acc, file) => acc + file.size, 0);
268+
269+
if (totalSize > MAX_UPLOAD_SIZE) {
270+
toaster.show(
271+
t(
272+
'upload_size_limit_exceeded',
273+
'Upload size limit exceeded. Maximum 1 GB per upload session.'
274+
),
275+
'warning'
276+
);
277+
return;
278+
}
279+
280+
setLoading(true);
281+
282+
// @ts-ignore
283+
uppy.addFiles(files);
284+
},
285+
[toaster, t]
286+
);
263287

264288
const dragAndDrop = useCallback(
265289
async (event: ClipboardEvent<HTMLDivElement> | File[]) => {
@@ -272,7 +296,7 @@ export const MediaBox: FC<{
272296
return;
273297
}
274298

275-
const files = [];
299+
const files: File[] = [];
276300
// @ts-ignore
277301
for (const item of clipboardItems) {
278302
if (item.kind === 'file') {
@@ -283,11 +307,26 @@ export const MediaBox: FC<{
283307
}
284308
}
285309

286-
for (const file of files.slice(0, 5)) {
310+
const totalSize = files.reduce((acc, file) => acc + file.size, 0);
311+
312+
if (totalSize > MAX_UPLOAD_SIZE) {
313+
toaster.show(
314+
t(
315+
'upload_size_limit_exceeded',
316+
'Upload size limit exceeded. Maximum 1 GB per upload session.'
317+
),
318+
'warning'
319+
);
320+
return;
321+
}
322+
323+
setLoading(true);
324+
325+
for (const file of files) {
287326
uppy.addFile(file);
288327
}
289328
},
290-
[]
329+
[toaster, t]
291330
);
292331

293332
const maximize = useCallback(
@@ -299,7 +338,10 @@ export const MediaBox: FC<{
299338
children: (
300339
<div className="w-full h-full p-[50px]">
301340
{media.path.indexOf('mp4') > -1 ? (
302-
<VideoFrame autoplay={true} url={mediaDirectory.set(media.path)} />
341+
<VideoFrame
342+
autoplay={true}
343+
url={mediaDirectory.set(media.path)}
344+
/>
303345
) : (
304346
<img
305347
width="100%"
@@ -340,17 +382,24 @@ export const MediaBox: FC<{
340382
const btn = useMemo(() => {
341383
return (
342384
<button
385+
disabled={loading}
343386
onClick={() => uploaderRef?.current?.click()}
344-
className="cursor-pointer bg-btnSimple changeColor flex gap-[8px] h-[44px] px-[18px] justify-center items-center rounded-[8px]"
387+
className="relative cursor-pointer bg-btnSimple changeColor flex gap-[8px] h-[44px] px-[18px] justify-center items-center rounded-[8px]"
345388
>
346-
<PlusIcon size={14} />
347-
<div>{t('upload', 'Upload')}</div>
389+
{loading ? (
390+
<div className="absolute left-[50%] top-[50%] -translate-y-[50%] -translate-x-[50%]">
391+
<div className="animate-spin h-[20px] w-[20px] border-4 border-white border-t-transparent rounded-full" />
392+
</div>
393+
) : (
394+
<PlusIcon size={14} />
395+
)}
396+
<div className={loading && 'invisible'}>{t('upload', 'Upload')}</div>
348397
</button>
349398
);
350-
}, [t]);
399+
}, [t, loading]);
351400

352401
return (
353-
<DropFiles className="flex flex-col flex-1" onDrop={dragAndDrop}>
402+
<DropFiles disabled={loading} className="flex flex-col flex-1" onDrop={dragAndDrop}>
354403
<div className="flex flex-col flex-1">
355404
<div
356405
className={clsx(
@@ -361,8 +410,8 @@ export const MediaBox: FC<{
361410
{!isLoading && !!data?.results?.length && (
362411
<div className="flex-1 text-[14px] font-[600] whitespace-pre-line">
363412
{t(
364-
'select_or_upload_pictures_max_5',
365-
'Select or upload pictures (maximum 5 at a time).'
413+
'select_or_upload_pictures_max_1gb',
414+
'Select or upload pictures (maximum 1 GB per upload).'
366415
)}
367416
{'\n'}
368417
{t(
@@ -423,8 +472,8 @@ export const MediaBox: FC<{
423472
</div>
424473
<div className="whitespace-pre-line text-newTextColor/[0.6] text-center">
425474
{t(
426-
'select_or_upload_pictures_max_5',
427-
'Select or upload pictures (maximum 5 at a time).'
475+
'select_or_upload_pictures_max_1gb',
476+
'Select or upload pictures (maximum 1 GB per upload).'
428477
)}{' '}
429478
{'\n'}
430479
{t(
@@ -476,7 +525,7 @@ export const MediaBox: FC<{
476525
onClick={addRemoveSelected(media)}
477526
>
478527
{!!selected.find((p: any) => p.id === media.id) ? (
479-
<div className="text-white flex justify-center items-center text-[14px] font-[500] w-[24px] h-[24px] rounded-full bg-[#612BD3] absolute -bottom-[10px] -end-[10px]">
528+
<div className="text-white flex z-[101] justify-center items-center text-[14px] font-[500] w-[24px] h-[24px] rounded-full bg-[#612BD3] absolute -bottom-[10px] -end-[10px]">
480529
{selected.findIndex((z: any) => z.id === media.id) + 1}
481530
</div>
482531
) : (

0 commit comments

Comments
 (0)