support drag&drop of folders to upload
Massimo Melina committed
Apr 9, 2024 at 11:27 UTC
eb6da5b293e05793b4171b899149ce47cf09ec67
2 files changed
+19
-8
frontend/src/BrowseFiles.ts
+2
-2
@@ -26,8 +26,8 @@ export function BrowseFiles() {
26
const { props, tile_size=0 } = useSnapState()
27
const propsDropFiles = useMemo(() => ({
28
id: 'files-dropper',
29
- ...acceptDropFiles(files =>
30
- props?.can_upload ? enqueue(files.map(file => ({ file })))
29
+ ...acceptDropFiles((files, to) =>
30
+ props?.can_upload ? enqueue(files.map(file => ({ file })), location.pathname + to)
31
: alertDialog(t("Upload not available"), 'warning')
32
),
33
}), [props])
frontend/src/upload.ts
+17
-6
@@ -18,7 +18,7 @@ import { subscribeKey } from 'valtio/utils'
18
19
const renameEnabled = getHFS().dontOverwriteUploading
20
21
-interface ToUpload { file: File, comment?: string, name?: string }
21
+interface ToUpload { file: File, comment?: string, name?: string, to?: string }
22
export const uploadState = proxy<{
23
done: number
24
doneByte: number
@@ -114,7 +114,7 @@ export function showUpload() {
114
const size = formatBytes(adding.reduce((a, x) => a + x.file.size, 0))
115
const isMobile = useIsMobile()
116
117
- return h(FlexV, { gap: '.5em', props: acceptDropFiles(more => uploadState.adding.push(...more.map(f => ({ file: ref(f) })))) },
117
+ return h(FlexV, { gap: '.5em', props: acceptDropFiles((files, to) => uploadState.adding.push(...files.map(f => ({ file: ref(f), to })))) },
118
h(FlexV, { className: 'upload-toolbar' },
119
!props?.can_upload ? t('no_upload_here', "No upload permission for the current folder")
120
: h(FlexV, {},
@@ -273,13 +273,12 @@ subscribe(uploadState, () => {
273
void startUpload(cur.entries[0], cur.to)
274
})
275
276
-export async function enqueue(entries: ToUpload[]) {
276
+export async function enqueue(entries: ToUpload[], to=location.pathname) {
277
if (_.remove(entries, x => !simulateBrowserAccept(x.file)).length)
278
await alertDialog(t('upload_file_rejected', "Some files were not accepted"), 'warning')
279
280
entries = _.uniqBy(entries, x => path(x.file))
281
if (!entries.length) return
282
- const to = location.pathname
282
const q = _.find(uploadState.qs, { to })
283
if (!q)
284
return uploadState.qs.push({ to, entries: entries.map(ref) })
@@ -428,7 +427,7 @@ function abortCurrentUpload() {
427
req?.abort()
428
}
429
431
-export function acceptDropFiles(cb: false | undefined | ((files:File[]) => void)) {
430
+export function acceptDropFiles(cb: false | undefined | ((files:File[], to: string) => void)) {
431
return {
432
onDragOver(ev: DragEvent) {
433
ev.preventDefault()
@@ -436,7 +435,19 @@ export function acceptDropFiles(cb: false | undefined | ((files:File[]) => void)
435
},
436
onDrop(ev: DragEvent) {
437
ev.preventDefault()
439
- cb && cb(Array.from(ev.dataTransfer!.files))
438
+ if (!cb) return
439
+ for (const it of ev.dataTransfer.items) {
440
+ const entry = it.webkitGetAsEntry()
441
+ if (entry)
442
+ (function recur(entry: FileSystemEntry, to = '') {
443
+ if (entry.isFile)
444
+ (entry as FileSystemFileEntry).file(x => cb([x], to))
445
+ else (entry as FileSystemDirectoryEntry).createReader?.().readEntries(entries => {
446
+ for (const e of entries)
447
+ recur(e, to + entry.name + '/')
448
+ })
449
+ })(entry)
450
+ }
451
},
452
}
453
}