@samitouri / QOSami-HFS / commits / eb6da5b2

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 }