Skip to content

Commit 1cfa410

Browse files
committed
Show an error if a user attempts to upload a folder
1 parent 1d5d92f commit 1cfa410

File tree

2 files changed

+27
-39
lines changed

2 files changed

+27
-39
lines changed

resources/scripts/components/server/files/UploadButton.tsx

Lines changed: 24 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import styled from 'styled-components/macro';
77
import { ModalMask } from '@/components/elements/Modal';
88
import Fade from '@/components/elements/Fade';
99
import useEventListener from '@/plugins/useEventListener';
10-
import useFlash from '@/plugins/useFlash';
10+
import { useFlashKey } from '@/plugins/useFlash';
1111
import useFileManagerSwr from '@/plugins/useFileManagerSwr';
1212
import { ServerContext } from '@/state/server';
1313
import { WithClassname } from '@/components/types';
@@ -23,23 +23,15 @@ function isFileOrDirectory(event: DragEvent): boolean {
2323
return false;
2424
}
2525

26-
for (let i = 0; i < event.dataTransfer.types.length; i++) {
27-
// Check if the item being dragged is not a file.
28-
// On Firefox a file of type "application/x-moz-file" is also in the array.
29-
if (event.dataTransfer.types[i] !== 'Files' && event.dataTransfer.types[i] !== 'application/x-moz-file') {
30-
return false;
31-
}
32-
}
33-
34-
return true;
26+
return event.dataTransfer.types.some((value) => value.toLowerCase() === 'files');
3527
}
3628

3729
export default ({ className }: WithClassname) => {
3830
const fileUploadInput = useRef<HTMLInputElement>(null);
3931
const [timeouts, setTimeouts] = useState<NodeJS.Timeout[]>([]);
4032
const [visible, setVisible] = useState(false);
4133
const { mutate } = useFileManagerSwr();
42-
const { clearFlashes, clearAndAddHttpError } = useFlash();
34+
const { addError, clearAndAddHttpError } = useFlashKey('files');
4335

4436
const uuid = ServerContext.useStoreState((state) => state.server.data!.uuid);
4537
const directory = ServerContext.useStoreState((state) => state.files.directory);
@@ -49,27 +41,17 @@ export default ({ className }: WithClassname) => {
4941
useEventListener(
5042
'dragenter',
5143
(e) => {
52-
if (!isFileOrDirectory(e)) {
53-
return;
54-
}
44+
e.preventDefault();
5545
e.stopPropagation();
56-
setVisible(true);
57-
},
58-
true
59-
);
60-
61-
useEventListener(
62-
'dragexit',
63-
(e) => {
64-
if (!isFileOrDirectory(e)) {
65-
return;
46+
if (isFileOrDirectory(e)) {
47+
return setVisible(true);
6648
}
67-
e.stopPropagation();
68-
setVisible(false);
6949
},
70-
true
50+
{ capture: true }
7151
);
7252

53+
useEventListener('dragexit', () => setVisible(false), { capture: true });
54+
7355
useEffect(() => {
7456
if (!visible) return;
7557

@@ -87,12 +69,23 @@ export default ({ className }: WithClassname) => {
8769

8870
const onFileSubmission = (files: FileList) => {
8971
const formData: FormData[] = [];
72+
73+
clearAndAddHttpError();
74+
const list = Array.from(files);
75+
if (list.some((file) => !file.type && file.size % 4096 === 0)) {
76+
return addError('Folder uploads are not supported at this time.', 'Error');
77+
}
78+
9079
Array.from(files).forEach((file) => {
9180
const form = new FormData();
9281
form.append('files', file);
9382
formData.push(form);
9483
});
95-
clearFlashes('files');
84+
85+
if (formData.length === 0) {
86+
return;
87+
}
88+
9689
Promise.all(
9790
Array.from(formData).map((f) =>
9891
getFileUploadUrl(uuid).then((url) =>
@@ -102,14 +95,10 @@ export default ({ className }: WithClassname) => {
10295
// @ts-expect-error this is valid
10396
const name = f.getAll('files')[0].name;
10497

105-
appendFileUpload({
106-
name: name,
107-
loaded: data.loaded,
108-
total: data.total,
109-
});
98+
appendFileUpload({ name: name, loaded: data.loaded, total: data.total });
11099

111100
if (data.loaded === data.total) {
112-
const timeout = setTimeout(() => removeFileUpload(name), 2000);
101+
const timeout = setTimeout(() => removeFileUpload(name), 500);
113102
setTimeouts((t) => [...t, timeout]);
114103
}
115104
},
@@ -118,10 +107,7 @@ export default ({ className }: WithClassname) => {
118107
)
119108
)
120109
.then(() => mutate())
121-
.catch((error) => {
122-
console.error(error);
123-
clearAndAddHttpError({ error, key: 'files' });
124-
});
110+
.catch(clearAndAddHttpError);
125111
};
126112

127113
return (

resources/scripts/plugins/useFlash.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { FlashStore } from '@/state/flashes';
33
import { ApplicationStore } from '@/state';
44

55
interface KeyedFlashStore {
6+
addError: (message: string, title?: string) => void;
67
clearFlashes: () => void;
78
clearAndAddHttpError: (error?: Error | string | null) => void;
89
}
@@ -12,9 +13,10 @@ const useFlash = (): Actions<FlashStore> => {
1213
};
1314

1415
const useFlashKey = (key: string): KeyedFlashStore => {
15-
const { clearFlashes, clearAndAddHttpError } = useFlash();
16+
const { addFlash, clearFlashes, clearAndAddHttpError } = useFlash();
1617

1718
return {
19+
addError: (message, title) => addFlash({ key, message, title, type: 'error' }),
1820
clearFlashes: () => clearFlashes(key),
1921
clearAndAddHttpError: (error) => clearAndAddHttpError({ key, error }),
2022
};

0 commit comments

Comments
 (0)