diff --git a/tabby-core/src/api/platform.ts b/tabby-core/src/api/platform.ts index f6f9044c..de8389d8 100644 --- a/tabby-core/src/api/platform.ts +++ b/tabby-core/src/api/platform.ts @@ -109,22 +109,50 @@ export abstract class PlatformService { abstract startDownload (name: string, mode: number, size: number): Promise abstract startUpload (options?: FileUploadOptions): Promise - startUploadFromDragEvent (event: DragEvent, multiple = false): FileUpload[] { + async startUploadFromDragEvent(event: DragEvent, multiple = false): Promise { const result: FileUpload[] = [] + if (!event.dataTransfer) { - return [] + return Promise.resolve([]) } - // eslint-disable-next-line @typescript-eslint/prefer-for-of - for (let i = 0; i < event.dataTransfer.files.length; i++) { - const file = event.dataTransfer.files[i] - const transfer = new HTMLFileUpload(file) - this.fileTransferStarted.next(transfer) - result.push(transfer) - if (!multiple) { - break + + const traverseFileTree = (item: any, path = ''): Promise => { + return new Promise((resolve) => { + if (item.isFile) { + item.file((file: File) => { + const transfer = new HTMLFileUpload(file, `${path}/${item.name}`) + this.fileTransferStarted.next(transfer) + result.push(transfer) + resolve() + }); + } else if (item.isDirectory) { + const dirReader = item.createReader() + dirReader.readEntries(async (entries: any[]) => { + for (const entry of entries) { + await traverseFileTree(entry, `${path}${item.name}/`) + } + resolve() + }) + } else { + resolve() + } + }) + } + + const promises: Promise[] = [] + + const items = event.dataTransfer.items + for (let i = 0; i < items.length; i++) { + const item = items[i].webkitGetAsEntry() + if (item) { + promises.push(traverseFileTree(item)) + if (!multiple) { + break + } } } - return result + + return Promise.all(promises).then(() => result) } getConfigPath (): string|null { @@ -194,7 +222,7 @@ export class HTMLFileUpload extends FileUpload { private stream: ReadableStream private reader: ReadableStreamDefaultReader - constructor (private file: File) { + constructor (private file: File, private relativePath: string|null = null) { super() this.stream = this.file.stream() this.reader = this.stream.getReader() @@ -204,8 +232,8 @@ export class HTMLFileUpload extends FileUpload { return this.file.name } - getRelativePath (): null { - return null + getRelativePath (): string|null { + return this.relativePath } getMode (): number { diff --git a/tabby-core/src/directives/dropZone.directive.ts b/tabby-core/src/directives/dropZone.directive.ts index 1da823eb..55e65449 100644 --- a/tabby-core/src/directives/dropZone.directive.ts +++ b/tabby-core/src/directives/dropZone.directive.ts @@ -27,9 +27,9 @@ export class DropZoneDirective implements AfterViewInit { }) } }) - this.el.nativeElement.addEventListener('drop', (event: DragEvent) => { + this.el.nativeElement.addEventListener('drop', async (event: DragEvent) => { this.removeHint() - for (const transfer of this.platform.startUploadFromDragEvent(event, true)) { + for (const transfer of await this.platform.startUploadFromDragEvent(event, true)) { this.transfer.emit(transfer) } }) diff --git a/tabby-electron/src/services/platform.service.ts b/tabby-electron/src/services/platform.service.ts index 701136ec..a330713b 100644 --- a/tabby-electron/src/services/platform.service.ts +++ b/tabby-electron/src/services/platform.service.ts @@ -301,7 +301,7 @@ class ElectronFileUpload extends FileUpload { private buffer: Buffer private powerSaveBlocker = 0 - constructor (private filePath: string, private electron: ElectronService, private relativePath = '') { + constructor (private filePath: string, private electron: ElectronService, private relativePath: string|null = null ) { super() this.buffer = Buffer.alloc(256 * 1024) this.powerSaveBlocker = electron.powerSaveBlocker.start('prevent-app-suspension') @@ -318,7 +318,7 @@ class ElectronFileUpload extends FileUpload { return path.basename(this.filePath) } - getRelativePath (): string { + getRelativePath (): string|null { return this.relativePath } diff --git a/tabby-ssh/src/components/sftpPanel.component.pug b/tabby-ssh/src/components/sftpPanel.component.pug index 95be8cb6..8e5716f9 100644 --- a/tabby-ssh/src/components/sftpPanel.component.pug +++ b/tabby-ssh/src/components/sftpPanel.component.pug @@ -31,7 +31,7 @@ button.btn.btn-link.text-decoration-none((click)='close()') !{require('../../../tabby-core/src/icons/times.svg')} -.body(dropZone, (transfer)='uploadOne($event)') +.body(dropZone, (transfer)='uploadOneWithFolder($event)') a.alert.alert-info.d-flex.align-items-center( *ngIf='shouldShowCWDTip && !cwdDetectionAvailable', (click)='platform.openExternal("https://tabby.sh/go/cwd-detection")' diff --git a/tabby-web/src/platform.ts b/tabby-web/src/platform.ts index d95d4f73..9a57e020 100644 --- a/tabby-web/src/platform.ts +++ b/tabby-web/src/platform.ts @@ -151,6 +151,7 @@ class HTMLFileDownload extends FileDownload { private name: string, private mode: number, private size: number, + private relativePath: string|null = null, ) { super() } @@ -159,8 +160,8 @@ class HTMLFileDownload extends FileDownload { return this.name } - getRelativePath (): null { - return null + getRelativePath (): string|null { + return this.relativePath } getMode (): number {