From 9e17ce157d42063e77fdf1a3573e439c87675bcf Mon Sep 17 00:00:00 2001 From: Eugene Pankov Date: Sat, 12 Jun 2021 13:05:09 +0200 Subject: [PATCH] download/upload implementation for the web --- app/package.json | 1 - app/yarn.lock | 25 ----------- terminus-core/src/api/index.ts | 2 +- terminus-core/src/api/platform.ts | 10 +++-- terminus-web/src/platform.ts | 75 ++++++++++++++++++++++++++++++- 5 files changed, 81 insertions(+), 32 deletions(-) diff --git a/app/package.json b/app/package.json index 8f0e32d8..334bf094 100644 --- a/app/package.json +++ b/app/package.json @@ -36,7 +36,6 @@ "native-process-working-directory": "^1.0.2", "ngx-toastr": "^14.0.0", "npm": "6", - "path": "0.12.7", "rxjs": "^7.1.0", "ssh2": "^1.1.0", "yargs": "^17.0.1", diff --git a/app/yarn.lock b/app/yarn.lock index 3599c124..9dedf800 100644 --- a/app/yarn.lock +++ b/app/yarn.lock @@ -1423,11 +1423,6 @@ inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - ini@^1.3.4, ini@^1.3.5, ini@^1.3.8, ini@~1.3.0: version "1.3.8" resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" @@ -2652,14 +2647,6 @@ path-type@^2.0.0: dependencies: pify "^2.0.0" -path@0.12.7: - version "0.12.7" - resolved "https://registry.npmjs.org/path/-/path-0.12.7.tgz" - integrity sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8= - dependencies: - process "^0.11.1" - util "^0.10.3" - performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz" @@ -2713,11 +2700,6 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -process@^0.11.1: - version "0.11.10" - resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" - integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= - promise-inflight@^1.0.1, promise-inflight@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz" @@ -3600,13 +3582,6 @@ util-promisify@^2.1.0: dependencies: object.getownpropertydescriptors "^2.0.3" -util@^0.10.3: - version "0.10.4" - resolved "https://registry.npmjs.org/util/-/util-0.10.4.tgz" - integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== - dependencies: - inherits "2.0.3" - uuid@^3.0.1, uuid@^3.3.2, uuid@^3.3.3: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" diff --git a/terminus-core/src/api/index.ts b/terminus-core/src/api/index.ts index 6099d20f..27d0fad7 100644 --- a/terminus-core/src/api/index.ts +++ b/terminus-core/src/api/index.ts @@ -10,7 +10,7 @@ export { Theme } from './theme' export { TabContextMenuItemProvider } from './tabContextMenuProvider' export { SelectorOption } from './selector' export { CLIHandler, CLIEvent } from './cli' -export { PlatformService, ClipboardContent, MessageBoxResult, MessageBoxOptions, FileDownload, FileUpload, FileTransfer, FileUploadOptions } from './platform' +export { PlatformService, ClipboardContent, MessageBoxResult, MessageBoxOptions, FileDownload, FileUpload, FileTransfer, HTMLFileUpload, FileUploadOptions } from './platform' export { MenuItemOptions } from './menu' export { BootstrapData, BOOTSTRAP_DATA } from './mainProcess' export { HostWindowService } from './hostWindow' diff --git a/terminus-core/src/api/platform.ts b/terminus-core/src/api/platform.ts index f169fdf2..e4daf242 100644 --- a/terminus-core/src/api/platform.ts +++ b/terminus-core/src/api/platform.ts @@ -88,7 +88,7 @@ export abstract class PlatformService { abstract startDownload (name: string, size: number): Promise abstract startUpload (options?: FileUploadOptions): Promise - startUploadFromDragEvent (event: DragEvent): FileUpload[] { + startUploadFromDragEvent (event: DragEvent, multiple = false): FileUpload[] { const result: FileUpload[] = [] if (!event.dataTransfer) { return [] @@ -96,9 +96,12 @@ export abstract class PlatformService { // 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 DropUpload(file) + const transfer = new HTMLFileUpload(file) this.fileTransferStarted.next(transfer) result.push(transfer) + if (!multiple) { + break + } } return result } @@ -160,8 +163,7 @@ export abstract class PlatformService { abstract quit (): void } - -class DropUpload extends FileUpload { +export class HTMLFileUpload extends FileUpload { private stream: ReadableStream private reader: ReadableStreamDefaultReader diff --git a/terminus-web/src/platform.ts b/terminus-web/src/platform.ts index 3ea32b8f..a13f8f6c 100644 --- a/terminus-web/src/platform.ts +++ b/terminus-web/src/platform.ts @@ -2,7 +2,7 @@ import '@vaadin/vaadin-context-menu/vaadin-context-menu.js' import copyToClipboard from 'copy-text-to-clipboard' import { Injectable } from '@angular/core' import { NgbModal } from '@ng-bootstrap/ng-bootstrap' -import { PlatformService, ClipboardContent, MenuItemOptions, MessageBoxOptions, MessageBoxResult } from 'terminus-core' +import { PlatformService, ClipboardContent, MenuItemOptions, MessageBoxOptions, MessageBoxResult, FileUpload, FileUploadOptions, FileDownload, HTMLFileUpload } from 'terminus-core' // eslint-disable-next-line no-duplicate-imports import type { ContextMenuElement, ContextMenuItem } from '@vaadin/vaadin-context-menu/vaadin-context-menu.js' @@ -14,6 +14,7 @@ import './styles.scss' export class WebPlatformService extends PlatformService { private menu: ContextMenuElement private contextMenuHandlers = new Map void>() + private fileSelector: HTMLInputElement constructor ( private ngbModal: NgbModal, @@ -24,6 +25,11 @@ export class WebPlatformService extends PlatformService { this.contextMenuHandlers.get(e.detail.value)?.() }) document.body.appendChild(this.menu) + + this.fileSelector = document.createElement('input') + this.fileSelector.type = 'file' + this.fileSelector.style.visibility = 'hidden' + document.body.appendChild(this.fileSelector) } readClipboard (): string { @@ -99,4 +105,71 @@ export class WebPlatformService extends PlatformService { quit (): void { window.close() } + + async startDownload (name: string, size: number): Promise { + const transfer = new HTMLFileDownload(name, size) + this.fileTransferStarted.next(transfer) + return transfer + } + + startUpload (options?: FileUploadOptions): Promise { + return new Promise(resolve => { + this.fileSelector.onchange = () => { + const transfers: FileUpload[] = [] + const fileList = this.fileSelector.files! + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + for (let i = 0; i < (fileList.length ?? 0); i++) { + const file = fileList[i] + const transfer = new HTMLFileUpload(file) + this.fileTransferStarted.next(transfer) + transfers.push(transfer) + if (!options?.multiple) { + break + } + } + resolve(transfers) + } + this.fileSelector.click() + }) + } +} + +class HTMLFileDownload extends FileDownload { + private buffers: Buffer[] = [] + + constructor ( + private name: string, + private size: number, + ) { + super() + } + + getName (): string { + return this.name + } + + getSize (): number { + return this.size + } + + async write (buffer: Buffer): Promise { + this.buffers.push(Buffer.from(buffer)) + this.increaseProgress(buffer.length) + if (this.isComplete()) { + this.finish() + } + } + + finish () { + const blob = new Blob(this.buffers, { type: 'application/octet-stream' }) + const element = window.document.createElement('a') + element.href = window.URL.createObjectURL(blob) + element.download = this.name + document.body.appendChild(element) + element.click() + document.body.removeChild(element) + } + + // eslint-disable-next-line @typescript-eslint/no-empty-function + close (): void { } }