mirror of
https://github.com/finos/SymphonyElectron.git
synced 2024-12-27 01:11:13 -06:00
Electron 44 File Download Experience (#116)
* Implemented File Download Experience 1. Initiate download manager when a file download is initiated. 2. Add items to download manager when new items are downloaded. 3. Allow user to open file in default OS app. 4. Allow user to show file in finder/explorer. 5. Allow user to close download bar. * 1. Removed underscore 2. Added safety checks 3. Added support for popouts 4. Creating most elements of download manager if electron * 1. Moved download manager logic to a separate file * 1. Added styles to help pushing up the content of the app when the download manager is being shown. * 1. Added tests for Download Manager. * Removed unnecessary code. * Made adjustments to handle positioning of the download manager through the flexbox model rather than the fixed positioning way. * Removed data attributes being added to body to handle download manager.
This commit is contained in:
parent
150ebaf70a
commit
299e75eca3
188
js/downloadManager/downloadManager.js
Normal file
188
js/downloadManager/downloadManager.js
Normal file
@ -0,0 +1,188 @@
|
||||
'use strict';
|
||||
|
||||
const { ipcRenderer, remote } = require('electron');
|
||||
|
||||
const local = {
|
||||
ipcRenderer: ipcRenderer,
|
||||
downloadItems: []
|
||||
};
|
||||
|
||||
// listen for file download complete event
|
||||
local.ipcRenderer.on('downloadCompleted', (event, arg) => {
|
||||
createDOM(arg);
|
||||
});
|
||||
|
||||
// listen for file download progress event
|
||||
local.ipcRenderer.on('downloadProgress', () => {
|
||||
initiate();
|
||||
});
|
||||
|
||||
/**
|
||||
* Open file in default app.
|
||||
*/
|
||||
function openFile(id) {
|
||||
let fileIndex = local.downloadItems.findIndex((item) => {
|
||||
return item._id === id
|
||||
});
|
||||
if (fileIndex !== -1){
|
||||
let openResponse = remote.shell.openExternal(`file:///${local.downloadItems[fileIndex].savedPath}`);
|
||||
if (!openResponse) {
|
||||
remote.dialog.showErrorBox("File not found", 'The file you are trying to open cannot be found in the specified path.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show downloaded file in explorer or finder.
|
||||
*/
|
||||
function showInFinder(id) {
|
||||
let showFileIndex = local.downloadItems.findIndex((item) => {
|
||||
return item._id === id
|
||||
});
|
||||
if (showFileIndex !== -1) {
|
||||
let showResponse = remote.shell.showItemInFolder(local.downloadItems[showFileIndex].savedPath);
|
||||
if (!showResponse) {
|
||||
remote.dialog.showErrorBox("File not found", 'The file you are trying to access cannot be found in the specified path.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createDOM(arg) {
|
||||
|
||||
if (arg && arg._id) {
|
||||
|
||||
local.downloadItems.push(arg);
|
||||
let downloadItemKey = arg._id;
|
||||
|
||||
let ul = document.getElementById('download-main');
|
||||
if (ul) {
|
||||
|
||||
let li = document.createElement('li');
|
||||
li.id = downloadItemKey;
|
||||
li.classList.add('download-element');
|
||||
ul.insertBefore(li, ul.childNodes[0]);
|
||||
|
||||
let itemDiv = document.createElement('div');
|
||||
itemDiv.classList.add('download-item');
|
||||
itemDiv.id = 'dl-item';
|
||||
li.appendChild(itemDiv);
|
||||
let openMainFile = document.getElementById('dl-item');
|
||||
openMainFile.addEventListener('click', () => {
|
||||
let id = openMainFile.parentNode.id;
|
||||
openFile(id);
|
||||
});
|
||||
|
||||
let fileDetails = document.createElement('div');
|
||||
fileDetails.classList.add('file');
|
||||
itemDiv.appendChild(fileDetails);
|
||||
|
||||
let downProgress = document.createElement('div');
|
||||
downProgress.id = 'download-progress';
|
||||
downProgress.classList.add('download-complete');
|
||||
downProgress.classList.add('flash');
|
||||
setTimeout(() => {
|
||||
downProgress.classList.remove('flash');
|
||||
}, 4000);
|
||||
fileDetails.appendChild(downProgress);
|
||||
|
||||
let fileIcon = document.createElement('span');
|
||||
fileIcon.classList.add('tempo-icon');
|
||||
fileIcon.classList.add('tempo-icon--download');
|
||||
fileIcon.classList.add('download-complete-color');
|
||||
setTimeout(() => {
|
||||
fileIcon.classList.remove('download-complete-color');
|
||||
fileIcon.classList.remove('tempo-icon--download');
|
||||
fileIcon.classList.add('tempo-icon--document');
|
||||
}, 4000);
|
||||
downProgress.appendChild(fileIcon);
|
||||
|
||||
let fileNameDiv = document.createElement('div');
|
||||
fileNameDiv.classList.add('downloaded-filename');
|
||||
itemDiv.appendChild(fileNameDiv);
|
||||
|
||||
let h2FileName = document.createElement('h2');
|
||||
h2FileName.classList.add('text-cutoff');
|
||||
h2FileName.innerHTML = arg.fileName;
|
||||
fileNameDiv.appendChild(h2FileName);
|
||||
|
||||
let fileProgressTitle = document.createElement('span');
|
||||
fileProgressTitle.id = 'per';
|
||||
fileProgressTitle.innerHTML = arg.total + ' Downloaded';
|
||||
fileNameDiv.appendChild(fileProgressTitle);
|
||||
|
||||
let caret = document.createElement('div');
|
||||
caret.id = 'menu';
|
||||
caret.classList.add('caret');
|
||||
caret.classList.add('tempo-icon');
|
||||
caret.classList.add('tempo-icon--dropdown');
|
||||
li.appendChild(caret);
|
||||
|
||||
let actionMenu = document.createElement('div');
|
||||
actionMenu.id = 'download-action-menu';
|
||||
actionMenu.classList.add('download-action-menu');
|
||||
caret.appendChild(actionMenu);
|
||||
|
||||
let caretUL = document.createElement('ul');
|
||||
caretUL.id = downloadItemKey;
|
||||
actionMenu.appendChild(caretUL);
|
||||
|
||||
let caretLiOpen = document.createElement('li');
|
||||
caretLiOpen.id = 'download-open';
|
||||
caretLiOpen.innerHTML = 'Open';
|
||||
caretUL.appendChild(caretLiOpen);
|
||||
let openFileDocument = document.getElementById('download-open');
|
||||
openFileDocument.addEventListener('click', () => {
|
||||
let id = openFileDocument.parentNode.id;
|
||||
openFile(id);
|
||||
});
|
||||
|
||||
let caretLiShow = document.createElement('li');
|
||||
caretLiShow.id = 'download-show-in-folder';
|
||||
caretLiShow.innerHTML = 'Show in Folder';
|
||||
caretUL.appendChild(caretLiShow);
|
||||
let showInFinderDocument = document.getElementById('download-show-in-folder');
|
||||
showInFinderDocument.addEventListener('click', () => {
|
||||
let id = showInFinderDocument.parentNode.id;
|
||||
showInFinder(id);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initiate() {
|
||||
let mainFooter = document.getElementById('footer');
|
||||
let mainDownloadDiv = document.getElementById('download-manager-footer');
|
||||
|
||||
if (mainDownloadDiv) {
|
||||
|
||||
mainFooter.classList.remove('hidden');
|
||||
|
||||
let ulFind = document.getElementById('download-main');
|
||||
|
||||
if (!ulFind){
|
||||
let uList = document.createElement('ul');
|
||||
uList.id = 'download-main';
|
||||
mainDownloadDiv.appendChild(uList);
|
||||
}
|
||||
|
||||
let closeSpanFind = document.getElementById('close-download-bar');
|
||||
|
||||
if (!closeSpanFind){
|
||||
let closeSpan = document.createElement('span');
|
||||
closeSpan.id = 'close-download-bar';
|
||||
closeSpan.classList.add('close-download-bar');
|
||||
closeSpan.classList.add('tempo-icon');
|
||||
closeSpan.classList.add('tempo-icon--close');
|
||||
mainDownloadDiv.appendChild(closeSpan);
|
||||
}
|
||||
|
||||
let closeDownloadManager = document.getElementById('close-download-bar');
|
||||
if (closeDownloadManager) {
|
||||
closeDownloadManager.addEventListener('click', () => {
|
||||
local.downloadItems = [];
|
||||
document.getElementById('footer').classList.add('hidden');
|
||||
document.getElementById('download-main').innerHTML = '';
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -11,6 +11,8 @@ const { isMac, isDevEnv } = require('./utils/misc.js');
|
||||
const protocolHandler = require('./protocolHandler');
|
||||
const getCmdLineArg = require('./utils/getCmdLineArg.js')
|
||||
|
||||
require('electron-dl')();
|
||||
|
||||
// used to check if a url was opened when the app was already open
|
||||
let isAppAlreadyOpen = false;
|
||||
|
||||
|
@ -19,6 +19,8 @@ const apiCmds = apiEnums.cmds;
|
||||
const apiName = apiEnums.apiName;
|
||||
const getMediaSources = require('../desktopCapturer/getSources');
|
||||
|
||||
require('../downloadManager/downloadManager');
|
||||
|
||||
const nodeURL = require('url');
|
||||
|
||||
// hold ref so doesn't get GC'ed
|
||||
|
@ -6,6 +6,7 @@ const BrowserWindow = electron.BrowserWindow;
|
||||
const path = require('path');
|
||||
const nodeURL = require('url');
|
||||
const querystring = require('querystring');
|
||||
const filesize = require('filesize');
|
||||
|
||||
const { getTemplate, getMinimizeOnClose } = require('./menus/menuTemplate.js');
|
||||
const loadErrors = require('./dialogs/showLoadError.js');
|
||||
@ -188,6 +189,25 @@ function doCreateMainWindow(initialUrl, initialBounds) {
|
||||
|
||||
mainWindow.on('closed', destroyAllWindows);
|
||||
|
||||
// Manage File Downloads
|
||||
mainWindow.webContents.session.on('will-download', (event, item, webContents) => {
|
||||
// When download is in progress, send necessary data to indicate the same
|
||||
webContents.send('downloadProgress');
|
||||
|
||||
// Send file path when download is complete
|
||||
item.once('done', (event, state) => {
|
||||
if (state === 'completed') {
|
||||
let data = {
|
||||
_id: getGuid(),
|
||||
savedPath: item.getSavePath() ? item.getSavePath() : '',
|
||||
total: filesize(item.getTotalBytes() ? item.getTotalBytes() : 0),
|
||||
fileName: item.getFilename() ? item.getFilename() : 'No name'
|
||||
};
|
||||
webContents.send('downloadCompleted', data);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// bug in electron is preventing this from working in sandboxed evt...
|
||||
// https://github.com/electron/electron/issues/8841
|
||||
mainWindow.webContents.on('will-navigate', function(event, willNavUrl) {
|
||||
|
@ -86,7 +86,9 @@
|
||||
"electron-context-menu": "^0.8.0",
|
||||
"electron-squirrel-startup": "^1.0.0",
|
||||
"keymirror": "0.1.1",
|
||||
"winreg": "^1.2.3"
|
||||
"winreg": "^1.2.3",
|
||||
"electron-dl": "^1.9.0",
|
||||
"filesize": "^3.5.10"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"screen-snippet": "git+https://github.com/symphonyoss/ScreenSnippet.git#v1.0.1"
|
||||
|
95
tests/DownloadManager.test.js
Normal file
95
tests/DownloadManager.test.js
Normal file
@ -0,0 +1,95 @@
|
||||
const downloadManager = require('../js/downloadManager/downloadManager');
|
||||
const electron = require('./__mocks__/electron');
|
||||
const $ = require('jquery');
|
||||
|
||||
describe('download manager', function() {
|
||||
|
||||
describe('Download Manager to create DOM once download is initiated', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
global.document.body.innerHTML =
|
||||
'<div id="download-main">' +
|
||||
'</div>';
|
||||
});
|
||||
|
||||
it('should inject download bar element into DOM once download is initiated', function() {
|
||||
|
||||
electron.ipcRenderer.send('downloadCompleted', {_id: '12345', fileName: 'test', total: 100});
|
||||
|
||||
expect(document.getElementsByClassName('text-cutoff')[0].innerHTML).toBe('test');
|
||||
expect(document.getElementById('per').innerHTML).toBe('100 Downloaded');
|
||||
|
||||
});
|
||||
|
||||
it('should inject multiple download items during multiple downloads', function() {
|
||||
|
||||
electron.ipcRenderer.send('downloadCompleted', {_id: '12345', fileName: 'test', total: 100});
|
||||
electron.ipcRenderer.send('downloadCompleted', {_id: '67890', fileName: 'test1', total: 200});
|
||||
|
||||
let fileNames = document.getElementsByClassName('text-cutoff');
|
||||
|
||||
expect(fileNames[0].innerHTML).toBe('test1');
|
||||
expect(fileNames[1].innerHTML).toBe('test');
|
||||
expect(document.getElementById('per').innerHTML).toBe('100 Downloaded');
|
||||
|
||||
let downloadElements = document.getElementsByClassName('download-element');
|
||||
|
||||
expect(downloadElements[0].id).toBe('67890');
|
||||
expect(downloadElements[1].id).toBe('12345');
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('Download Manager to initiate footer', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
global.document.body.innerHTML =
|
||||
'<div id="download-manager-footer" class="hidden">' +
|
||||
'<div id="download-main">' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
});
|
||||
|
||||
it('should inject dom element once download is completed', function() {
|
||||
|
||||
electron.ipcRenderer.send('downloadProgress');
|
||||
|
||||
expect(document.getElementById('download-manager-footer').classList).not.toContain('hidden');
|
||||
|
||||
});
|
||||
|
||||
it('should remove the download bar and clear up the download items', function() {
|
||||
|
||||
electron.ipcRenderer.send('downloadProgress');
|
||||
|
||||
console.log(document.getElementById('download-manager-footer').classList);
|
||||
expect(document.getElementById('download-manager-footer').classList).not.toContain('hidden');
|
||||
|
||||
$('#close-download-bar').click();
|
||||
expect(document.getElementById('download-manager-footer').classList).toContain('hidden');
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('Download Manager to initiate footer', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
global.document.body.innerHTML =
|
||||
'<div id="download-manager-footer" class="hidden">' +
|
||||
'</div>';
|
||||
});
|
||||
|
||||
it('should inject ul element if not found', function() {
|
||||
|
||||
electron.ipcRenderer.send('downloadProgress');
|
||||
|
||||
expect(document.getElementById('download-main')).not.toBeNull();
|
||||
expect(document.getElementById('download-manager-footer').classList).not.toContain('hidden');
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
Loading…
Reference in New Issue
Block a user