From 29239a6ee7a102f08427e5a6c7e2bf02df331ec9 Mon Sep 17 00:00:00 2001 From: Uni Sayo Date: Tue, 16 Apr 2019 12:29:50 +0000 Subject: [PATCH] fix copy and download, move status bar items to notification --- .../file-download-command-contribution.ts | 18 ---- .../browser/download/file-download-service.ts | 94 ++++++++----------- .../node/download/file-download-handler.ts | 7 +- .../src/browser/navigator-contribution.ts | 4 - .../src/browser/workspace-commands.ts | 4 - 5 files changed, 44 insertions(+), 83 deletions(-) diff --git a/packages/filesystem/src/browser/download/file-download-command-contribution.ts b/packages/filesystem/src/browser/download/file-download-command-contribution.ts index 66dd30bc0f6ce..4e19defa86b9f 100644 --- a/packages/filesystem/src/browser/download/file-download-command-contribution.ts +++ b/packages/filesystem/src/browser/download/file-download-command-contribution.ts @@ -36,9 +36,7 @@ export class FileDownloadCommandContribution implements CommandContribution { registerCommands(registry: CommandRegistry): void { const handler = new UriAwareCommandHandler(this.selectionService, this.downloadHandler(), { multi: true }); - const linkHandler = new UriAwareCommandHandler(this.selectionService, this.downloadLinkHandler(), { multi: true }); registry.registerCommand(FileDownloadCommands.DOWNLOAD, handler); - registry.registerCommand(FileDownloadCommands.DOWNLOAD_LINK, linkHandler); registry.registerCommand(FileDownloadCommands.UPLOAD, new FileSelection.CommandHandler(this.selectionService, { multi: false, isEnabled: selection => this.canUpload(selection), @@ -73,16 +71,6 @@ export class FileDownloadCommandContribution implements CommandContribution { }; } - protected downloadLinkHandler(): UriCommandHandler { - return { - execute: uris => this.executeLinkDownload(uris), - isEnabled: uris => this.isDownloadEnabled(uris) && document.queryCommandSupported('copy'), - isVisible: uris => this.isDownloadVisible(uris) && document.queryCommandSupported('copy'), - }; - } - protected async executeLinkDownload(uris: URI[]): Promise { - this.downloadService.copyDownloadLink(uris); - } protected async executeDownload(uris: URI[]): Promise { this.downloadService.download(uris); } @@ -105,12 +93,6 @@ export namespace FileDownloadCommands { label: 'Download' }; - export const DOWNLOAD_LINK: Command = { - id: 'file.download.link', - category: 'File', - label: 'Copy Download Link' - }; - export const UPLOAD: Command = { id: 'file.upload', category: 'File', diff --git a/packages/filesystem/src/browser/download/file-download-service.ts b/packages/filesystem/src/browser/download/file-download-service.ts index 760178bfb9022..2d8b72afdf7f1 100644 --- a/packages/filesystem/src/browser/download/file-download-service.ts +++ b/packages/filesystem/src/browser/download/file-download-service.ts @@ -24,6 +24,8 @@ import { FileSystem } from '../../common/filesystem'; import { FileDownloadData } from '../../common/download/file-download-data'; import { Deferred } from '@theia/core/lib/common/promise-util'; import { MessageService } from '@theia/core/lib/common/message-service'; +import { addClipboardListener } from '@theia/core/lib/browser/widgets'; +import { DisposableCollection } from '@theia/core/lib/common'; @injectable() export class FileDownloadService { @@ -31,7 +33,6 @@ export class FileDownloadService { protected static PREPARING_DOWNLOAD_ID = 'theia-preparing-download'; protected statusBarTimeout: number | undefined; protected anchor: HTMLAnchorElement | undefined; - protected downloadQueue: number[] = []; protected downloadCounter: number = 0; @inject(ILogger) @@ -46,6 +47,8 @@ export class FileDownloadService { @inject(MessageService) protected readonly messageService: MessageService; + protected readonly toDispose = new DisposableCollection(); + protected uploadForm: { target: HTMLInputElement file: HTMLInputElement @@ -147,77 +150,56 @@ export class FileDownloadService { return this.deferredUpload.promise; } - async copyDownloadLink(uris: URI[]): Promise { - if (uris.length === 0) { - return; - } - try { - await this.statusBar.setElement(FileDownloadService.PREPARING_DOWNLOAD_ID, { - alignment: StatusBarAlignment.RIGHT, - text: '$(spinner~spin) Preparing download link...', - tooltip: 'Preparing download link...', - priority: 1 - }); - const response = await fetch(this.request(uris)); - const jsonResponse = await response.json(); - const { status, statusText } = response; - await this.statusBar.removeElement(FileDownloadService.PREPARING_DOWNLOAD_ID); - if (status === 200) { - const url = `${this.endpoint()}/download/?id=${jsonResponse.id}`; - const setClipBoardData = (event: ClipboardEvent) => { - event.clipboardData.setData('text/plain', url); - event.preventDefault(); - }; - document.addEventListener('copy', setClipBoardData); - document.execCommand('copy', false); - document.removeEventListener('copy', setClipBoardData); - this.showStatusMessage('Download link copied!', 5000, 'Download link successfully copied!'); - } else { - throw new Error(`Received unexpected status code: ${status}. [${statusText}]`); - } - } catch (e) { - this.logger.error(`Error occurred when getting the download link: ${uris.map(u => u.toString(true))}.`, e); + protected handleCopy(event: ClipboardEvent, downloadUrl: string) { + if (downloadUrl) { + event.clipboardData.setData('text/plain', downloadUrl); + event.preventDefault(); + this.messageService.info('Download link copied!'); } } + async cancelDownload(id: string) { + await fetch(`${this.endpoint()}/download/?id=${id}&cancel=true`); + } async download(uris: URI[]): Promise { + let cancel = false; if (uris.length === 0) { return; } - let downloadId: number | undefined; try { - downloadId = this.downloadCounter++; - if (this.downloadQueue.length === 0) { - await this.statusBar.setElement(FileDownloadService.PREPARING_DOWNLOAD_ID, { - alignment: StatusBarAlignment.RIGHT, - text: '$(spinner~spin) Preparing download...', - tooltip: 'Preparing download...', - priority: 1 - }); - } - this.downloadQueue.push(downloadId); - const response = await fetch(this.request(uris)); + const [progress, response] = await Promise.all([ + this.messageService.showProgress({ + text: 'Preparing download link...', options: { cancelable: true } + }, () => { cancel = true; }), + fetch(this.request(uris)) + ]); const jsonResponse = await response.json(); - await this.statusBar.removeElement(FileDownloadService.PREPARING_DOWNLOAD_ID); + if (cancel) { + this.cancelDownload(jsonResponse.id); + return; + } const { status, statusText } = response; if (status === 200) { - this.forceDownload(jsonResponse.id, decodeURIComponent(jsonResponse.name)); - this.showStatusMessage('Download started...', 2000); + progress.cancel(); + const downloadUrl = `${this.endpoint()}/download/?id=${jsonResponse.id}`; + this.messageService.info(downloadUrl, 'Download', 'Copy Download Link').then(action => { + if (action === 'Download') { + this.forceDownload(jsonResponse.id, decodeURIComponent(jsonResponse.name)); + this.messageService.info('Download started!'); + } else if (action === 'Copy Download Link') { + if (document.documentElement) { + addClipboardListener(document.documentElement, 'copy', e => this.handleCopy(e, downloadUrl)); + document.execCommand('copy'); + } + } else { + this.cancelDownload(jsonResponse.id); + } + }); } else { throw new Error(`Received unexpected status code: ${status}. [${statusText}]`); } } catch (e) { this.logger.error(`Error occurred when downloading: ${uris.map(u => u.toString(true))}.`, e); - } finally { - if (downloadId !== undefined) { - const indexOf = this.downloadQueue.indexOf(downloadId); - if (indexOf !== -1) { - this.downloadQueue.splice(indexOf, 1); - } - if (this.downloadQueue.length === 0) { - this.statusBar.removeElement(FileDownloadService.PREPARING_DOWNLOAD_ID); - } - } } } diff --git a/packages/filesystem/src/node/download/file-download-handler.ts b/packages/filesystem/src/node/download/file-download-handler.ts index ec286313a7b7c..a9cbc041f6e53 100644 --- a/packages/filesystem/src/node/download/file-download-handler.ts +++ b/packages/filesystem/src/node/download/file-download-handler.ts @@ -164,6 +164,7 @@ export class DownloadHandler extends FileDownloadHandler { this.handleError(response, `Cannot access the 'id' query from the request. The query was: ${JSON.stringify(query)}.`, BAD_REQUEST); return; } + const cancelDownload = query.cancel; const downloadInfo = this.fileDownloadCollection.getDownload(query.id); if (!downloadInfo) { this.handleError(response, `Cannot find the file from the request. The query was: ${JSON.stringify(query)}.`, NOT_FOUND); @@ -175,9 +176,13 @@ export class DownloadHandler extends FileDownloadHandler { response.status(OK).end(); return; } - if (downloadInfo && downloadInfo.file) { + if (downloadInfo && downloadInfo.file && !cancelDownload) { this.download(request, response, downloadInfo, query.id); } + if (cancelDownload) { + this.logger.info('Download', query.id, 'has been cancelled'); + this.fileDownloadCollection.deleteDownload(query.id); + } } } diff --git a/packages/navigator/src/browser/navigator-contribution.ts b/packages/navigator/src/browser/navigator-contribution.ts index e1625fdd2c5d0..6dbbd41ca1b22 100644 --- a/packages/navigator/src/browser/navigator-contribution.ts +++ b/packages/navigator/src/browser/navigator-contribution.ts @@ -202,10 +202,6 @@ export class FileNavigatorContribution extends AbstractViewContribution