mirror of
https://github.com/finos/SymphonyElectron.git
synced 2025-02-25 18:55:29 -06:00
feat: SDA-1995: implement download manager for Mana (#982)
* SDA-1995: add download handler functionality for Mana Signed-off-by: Vishwas Shashidhar <vishwas.shashidhar@symphony.com> * SDA-1995: add unit tests Signed-off-by: Vishwas Shashidhar <vishwas.shashidhar@symphony.com> * SDA-1995: fix unit tests on Windows Signed-off-by: Vishwas Shashidhar <vishwas.shashidhar@symphony.com> * SDA-1955: address PR comments Signed-off-by: Vishwas Shashidhar <vishwas.shashidhar@symphony.com>
This commit is contained in:
committed by
GitHub
parent
0921cca4b1
commit
8f518e3936
54
spec/downloadHandler.spec.ts
Normal file
54
spec/downloadHandler.spec.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
jest.mock('electron-log');
|
||||||
|
|
||||||
|
jest.mock('../src/app/window-handler', () => {
|
||||||
|
return {
|
||||||
|
windowHandler: {
|
||||||
|
setIsAutoReload: jest.fn(() => true),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.mock('../src/app/window-utils', () => {
|
||||||
|
return {
|
||||||
|
windowExists: jest.fn(() => true),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('download handler', () => {
|
||||||
|
let downloadHandlerInstance;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.resetModules();
|
||||||
|
// I did it for reset module imported between tests
|
||||||
|
const { downloadHandler } = require('../src/app/download-handler');
|
||||||
|
downloadHandlerInstance = downloadHandler;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll((done) => {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call `sendDownloadCompleted` when download succeeds', () => {
|
||||||
|
const spy: jest.SpyInstance = jest.spyOn(downloadHandlerInstance, 'sendDownloadCompleted')
|
||||||
|
.mockImplementation(() => jest.fn());
|
||||||
|
|
||||||
|
const data: any = {
|
||||||
|
_id: '121312-123912321-1231231',
|
||||||
|
savedPath: '/abc/def/123.txt',
|
||||||
|
total: '1234556',
|
||||||
|
fileName: 'Test.txt',
|
||||||
|
};
|
||||||
|
|
||||||
|
downloadHandlerInstance.onDownloadSuccess(data);
|
||||||
|
expect(spy).toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call `sendDownloadFailed` when download fails', () => {
|
||||||
|
const spy: jest.SpyInstance = jest.spyOn(downloadHandlerInstance, 'sendDownloadFailed')
|
||||||
|
.mockImplementation(() => jest.fn());
|
||||||
|
|
||||||
|
downloadHandlerInstance.onDownloadFailed();
|
||||||
|
expect(spy).toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { activityDetection } from '../src/app/activity-detection';
|
import { activityDetection } from '../src/app/activity-detection';
|
||||||
|
import { downloadHandler } from '../src/app/download-handler';
|
||||||
import '../src/app/main-api-handler';
|
import '../src/app/main-api-handler';
|
||||||
import { protocolHandler } from '../src/app/protocol-handler';
|
import { protocolHandler } from '../src/app/protocol-handler';
|
||||||
import { screenSnippet } from '../src/app/screen-snippet-handler';
|
import { screenSnippet } from '../src/app/screen-snippet-handler';
|
||||||
@@ -94,6 +95,17 @@ jest.mock('../src/app/activity-detection', () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
jest.mock('../src/app/download-handler', () => {
|
||||||
|
return {
|
||||||
|
downloadHandler: {
|
||||||
|
setWindow: jest.fn(),
|
||||||
|
openFile: jest.fn(),
|
||||||
|
showInFinder: jest.fn(),
|
||||||
|
clearDownloadItems: jest.fn(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
jest.mock('../src/common/i18n');
|
jest.mock('../src/common/i18n');
|
||||||
|
|
||||||
describe('main api handler', () => {
|
describe('main api handler', () => {
|
||||||
@@ -201,6 +213,67 @@ describe('main api handler', () => {
|
|||||||
expect(spy).toBeCalledWith(...expectedValue);
|
expect(spy).toBeCalledWith(...expectedValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should call `registerDownloadHandler` correctly', () => {
|
||||||
|
const spy = jest.spyOn(downloadHandler, 'setWindow');
|
||||||
|
const value = {
|
||||||
|
cmd: apiCmds.registerDownloadHandler,
|
||||||
|
};
|
||||||
|
const expectedValue = [ { send: expect.any(Function) } ];
|
||||||
|
ipcMain.send(apiName.symphonyApi, value);
|
||||||
|
expect(spy).toBeCalledWith(...expectedValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call `openFile` correctly', () => {
|
||||||
|
const spy = jest.spyOn(downloadHandler, 'openFile');
|
||||||
|
const value = {
|
||||||
|
cmd: apiCmds.openDownloadItem,
|
||||||
|
id: '12345678',
|
||||||
|
};
|
||||||
|
const expectedValue = '12345678';
|
||||||
|
ipcMain.send(apiName.symphonyApi, value);
|
||||||
|
expect(spy).toBeCalledWith(expectedValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not call `openFile` if id is not a string', () => {
|
||||||
|
const spy = jest.spyOn(downloadHandler, 'openFile');
|
||||||
|
const value = {
|
||||||
|
cmd: apiCmds.openDownloadItem,
|
||||||
|
id: 10,
|
||||||
|
};
|
||||||
|
ipcMain.send(apiName.symphonyApi, value);
|
||||||
|
expect(spy).not.toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call `showFile` correctly', () => {
|
||||||
|
const spy = jest.spyOn(downloadHandler, 'showInFinder');
|
||||||
|
const value = {
|
||||||
|
cmd: apiCmds.showDownloadItem,
|
||||||
|
id: `12345678`,
|
||||||
|
};
|
||||||
|
const expectedValue = '12345678';
|
||||||
|
ipcMain.send(apiName.symphonyApi, value);
|
||||||
|
expect(spy).toBeCalledWith(expectedValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not call `showFile` if id is not a string', () => {
|
||||||
|
const spy = jest.spyOn(downloadHandler, 'showInFinder');
|
||||||
|
const value = {
|
||||||
|
cmd: apiCmds.showDownloadItem,
|
||||||
|
id: 10,
|
||||||
|
};
|
||||||
|
ipcMain.send(apiName.symphonyApi, value);
|
||||||
|
expect(spy).not.toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call `clearItems` correctly', () => {
|
||||||
|
const spy = jest.spyOn(downloadHandler, 'clearDownloadItems');
|
||||||
|
const value = {
|
||||||
|
cmd: apiCmds.clearDownloadItems,
|
||||||
|
};
|
||||||
|
ipcMain.send(apiName.symphonyApi, value);
|
||||||
|
expect(spy).toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('should call `showNotificationSettings` correctly', () => {
|
it('should call `showNotificationSettings` correctly', () => {
|
||||||
const spy = jest.spyOn(windowHandler, 'createNotificationSettingsWindow');
|
const spy = jest.spyOn(windowHandler, 'createNotificationSettingsWindow');
|
||||||
const value = {
|
const value = {
|
||||||
|
|||||||
168
src/app/download-handler.ts
Normal file
168
src/app/download-handler.ts
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
import { BrowserWindow, dialog, shell } from 'electron';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import { i18n } from '../common/i18n';
|
||||||
|
import { logger } from '../common/logger';
|
||||||
|
import { windowExists } from './window-utils';
|
||||||
|
|
||||||
|
const DOWNLOAD_MANAGER_NAMESPACE = 'DownloadManager';
|
||||||
|
|
||||||
|
export interface IDownloadManager {
|
||||||
|
_id: string;
|
||||||
|
fileName: string;
|
||||||
|
fileDisplayName?: string;
|
||||||
|
savedPath: string;
|
||||||
|
total: string;
|
||||||
|
flashing?: boolean;
|
||||||
|
count?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DownloadHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks and constructs file name
|
||||||
|
*
|
||||||
|
* @param fileName {String} Filename
|
||||||
|
* @param item {IDownloadManager} Download Item
|
||||||
|
*/
|
||||||
|
private static getFileDisplayName(fileName: string, item: IDownloadManager): string {
|
||||||
|
/* If it exists, add a count to the name like how Chrome does */
|
||||||
|
if (item.count && item.count > 0) {
|
||||||
|
const extLastIndex = fileName.lastIndexOf('.');
|
||||||
|
const fileCount = ' (' + item.count + ')';
|
||||||
|
|
||||||
|
fileName = fileName.slice(0, extLastIndex) + fileCount + fileName.slice(extLastIndex);
|
||||||
|
}
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show dialog for failed cases
|
||||||
|
*/
|
||||||
|
private static async showDialog(): Promise<void> {
|
||||||
|
const focusedWindow = BrowserWindow.getFocusedWindow();
|
||||||
|
const message = i18n.t('The file you are trying to open cannot be found in the specified path.', DOWNLOAD_MANAGER_NAMESPACE)();
|
||||||
|
const title = i18n.t('File not Found', DOWNLOAD_MANAGER_NAMESPACE)();
|
||||||
|
|
||||||
|
if (!focusedWindow || !windowExists(focusedWindow)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await dialog.showMessageBox(focusedWindow, {
|
||||||
|
message,
|
||||||
|
title,
|
||||||
|
type: 'error',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private window!: Electron.WebContents | null;
|
||||||
|
private items: IDownloadManager[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the window for the download handler
|
||||||
|
* @param window Window object
|
||||||
|
*/
|
||||||
|
public setWindow(window: Electron.WebContents): void {
|
||||||
|
this.window = window;
|
||||||
|
logger.info(`download-handler: Initialized download handler`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the downloaded file
|
||||||
|
*
|
||||||
|
* @param id {string} File ID
|
||||||
|
*/
|
||||||
|
public openFile(id: string): void {
|
||||||
|
const filePath = this.getFilePath(id);
|
||||||
|
|
||||||
|
const openResponse = fs.existsSync(`${filePath}`) && shell.openItem(`${filePath}`);
|
||||||
|
if (openResponse) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadHandler.showDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the downloaded file in finder/explorer
|
||||||
|
*
|
||||||
|
* @param id {string} File ID
|
||||||
|
*/
|
||||||
|
public showInFinder(id: string): void {
|
||||||
|
const filePath = this.getFilePath(id);
|
||||||
|
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
shell.showItemInFolder(filePath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadHandler.showDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears download items
|
||||||
|
*/
|
||||||
|
public clearDownloadItems(): void {
|
||||||
|
this.items = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a successful download
|
||||||
|
* @param item Download item
|
||||||
|
*/
|
||||||
|
public onDownloadSuccess(item: IDownloadManager): void {
|
||||||
|
let itemCount = 0;
|
||||||
|
for (const existingItem of this.items) {
|
||||||
|
if (item.fileName === existingItem.fileName) {
|
||||||
|
itemCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
item.count = itemCount;
|
||||||
|
item.fileDisplayName = DownloadHandler.getFileDisplayName(item.fileName, item);
|
||||||
|
this.items.push(item);
|
||||||
|
this.sendDownloadCompleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a failed download
|
||||||
|
*/
|
||||||
|
public onDownloadFailed(): void {
|
||||||
|
this.sendDownloadFailed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send download completed event to the renderer process
|
||||||
|
*/
|
||||||
|
private sendDownloadCompleted(): void {
|
||||||
|
if (this.window && !this.window.isDestroyed()) {
|
||||||
|
logger.info(`download-handler: Download completed! Informing the client!`);
|
||||||
|
this.window.send('download-completed', this.items.map((item) => {
|
||||||
|
return {id: item._id, fileDisplayName: item.fileDisplayName, fileSize: item.total};
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send download failed event to the renderer process
|
||||||
|
*/
|
||||||
|
private sendDownloadFailed(): void {
|
||||||
|
if (this.window && !this.window.isDestroyed()) {
|
||||||
|
logger.info(`download-handler: Download failed! Informing the client!`);
|
||||||
|
this.window.send('download-failed');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get file path for the given item
|
||||||
|
* @param id ID of the item
|
||||||
|
*/
|
||||||
|
private getFilePath(id: string): string {
|
||||||
|
const fileIndex = this.items.findIndex((item) => {
|
||||||
|
return item._id === id;
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.items[fileIndex].savedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadHandler = new DownloadHandler();
|
||||||
|
export { downloadHandler };
|
||||||
@@ -6,6 +6,7 @@ import { logger } from '../common/logger';
|
|||||||
import { activityDetection } from './activity-detection';
|
import { activityDetection } from './activity-detection';
|
||||||
import { analytics } from './analytics-handler';
|
import { analytics } from './analytics-handler';
|
||||||
import { CloudConfigDataTypes, config, ICloudConfig } from './config-handler';
|
import { CloudConfigDataTypes, config, ICloudConfig } from './config-handler';
|
||||||
|
import { downloadHandler } from './download-handler';
|
||||||
import { memoryMonitor } from './memory-monitor';
|
import { memoryMonitor } from './memory-monitor';
|
||||||
import { protocolHandler } from './protocol-handler';
|
import { protocolHandler } from './protocol-handler';
|
||||||
import { finalizeLogExports, registerLogRetriever } from './reports-handler';
|
import { finalizeLogExports, registerLogRetriever } from './reports-handler';
|
||||||
@@ -85,6 +86,9 @@ ipcMain.on(apiName.symphonyApi, async (event: Electron.IpcMainEvent, arg: IApiAr
|
|||||||
activityDetection.setWindowAndThreshold(event.sender, arg.period);
|
activityDetection.setWindowAndThreshold(event.sender, arg.period);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case apiCmds.registerDownloadHandler:
|
||||||
|
downloadHandler.setWindow(event.sender);
|
||||||
|
break;
|
||||||
case apiCmds.showNotificationSettings:
|
case apiCmds.showNotificationSettings:
|
||||||
if (typeof arg.windowName === 'string') {
|
if (typeof arg.windowName === 'string') {
|
||||||
windowHandler.createNotificationSettingsWindow(arg.windowName);
|
windowHandler.createNotificationSettingsWindow(arg.windowName);
|
||||||
@@ -147,6 +151,19 @@ ipcMain.on(apiName.symphonyApi, async (event: Electron.IpcMainEvent, arg: IApiAr
|
|||||||
downloadManagerAction(arg.type, arg.path);
|
downloadManagerAction(arg.type, arg.path);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case apiCmds.openDownloadItem:
|
||||||
|
if (typeof arg.id === 'string') {
|
||||||
|
downloadHandler.openFile(arg.id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case apiCmds.showDownloadItem:
|
||||||
|
if (typeof arg.id === 'string') {
|
||||||
|
downloadHandler.showInFinder(arg.id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case apiCmds.clearDownloadItems:
|
||||||
|
downloadHandler.clearDownloadItems();
|
||||||
|
break;
|
||||||
case apiCmds.isMisspelled:
|
case apiCmds.isMisspelled:
|
||||||
if (typeof arg.word === 'string') {
|
if (typeof arg.word === 'string') {
|
||||||
event.returnValue = windowHandler.spellchecker ? windowHandler.spellchecker.isMisspelled(arg.word) : false;
|
event.returnValue = windowHandler.spellchecker ? windowHandler.spellchecker.isMisspelled(arg.word) : false;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { getGuid } from '../common/utils';
|
|||||||
import { whitelistHandler } from '../common/whitelist-handler';
|
import { whitelistHandler } from '../common/whitelist-handler';
|
||||||
import { autoLaunchInstance } from './auto-launch-controller';
|
import { autoLaunchInstance } from './auto-launch-controller';
|
||||||
import { CloudConfigDataTypes, config, IConfig, ICustomRectangle } from './config-handler';
|
import { CloudConfigDataTypes, config, IConfig, ICustomRectangle } from './config-handler';
|
||||||
|
import { downloadHandler, IDownloadManager } from './download-handler';
|
||||||
import { memoryMonitor } from './memory-monitor';
|
import { memoryMonitor } from './memory-monitor';
|
||||||
import { screenSnippet } from './screen-snippet-handler';
|
import { screenSnippet } from './screen-snippet-handler';
|
||||||
import { updateAlwaysOnTop } from './window-actions';
|
import { updateAlwaysOnTop } from './window-actions';
|
||||||
@@ -400,13 +401,30 @@ export const handleDownloadManager = (_event, item: Electron.DownloadItem, webCo
|
|||||||
// Send file path when download is complete
|
// Send file path when download is complete
|
||||||
item.once('done', (_e, state) => {
|
item.once('done', (_e, state) => {
|
||||||
if (state === 'completed') {
|
if (state === 'completed') {
|
||||||
const data = {
|
const data: IDownloadManager = {
|
||||||
_id: getGuid(),
|
_id: getGuid(),
|
||||||
savedPath: item.getSavePath() || '',
|
savedPath: item.getSavePath() || '',
|
||||||
total: filesize(item.getTotalBytes() || 0),
|
total: filesize(item.getTotalBytes() || 0),
|
||||||
fileName: item.getFilename() || 'No name',
|
fileName: item.getFilename() || 'No name',
|
||||||
};
|
};
|
||||||
|
logger.info('window-utils: Download completed, informing download manager');
|
||||||
webContents.send('downloadCompleted', data);
|
webContents.send('downloadCompleted', data);
|
||||||
|
downloadHandler.onDownloadSuccess(data);
|
||||||
|
} else {
|
||||||
|
logger.info('window-utils: Download failed, informing download manager');
|
||||||
|
downloadHandler.onDownloadFailed();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
item.on('updated', (_e, state) => {
|
||||||
|
if (state === 'interrupted') {
|
||||||
|
logger.info('window-utils: Download is interrupted but can be resumed');
|
||||||
|
} else if (state === 'progressing') {
|
||||||
|
if (item.isPaused()) {
|
||||||
|
logger.info('window-utils: Download is paused');
|
||||||
|
} else {
|
||||||
|
logger.info(`window-utils: Received bytes: ${item.getReceivedBytes()}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ export enum apiCmds {
|
|||||||
setCloudConfig = 'set-cloud-config',
|
setCloudConfig = 'set-cloud-config',
|
||||||
getCPUUsage = 'get-cpu-usage',
|
getCPUUsage = 'get-cpu-usage',
|
||||||
checkMediaPermission = 'check-media-permission',
|
checkMediaPermission = 'check-media-permission',
|
||||||
|
registerDownloadHandler = 'register-download-handler',
|
||||||
|
openDownloadItem = 'open-download-item',
|
||||||
|
showDownloadItem = 'show-download-item',
|
||||||
|
clearDownloadItems = 'clear-download-items',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum apiName {
|
export enum apiName {
|
||||||
@@ -151,6 +155,16 @@ export interface ICPUUsage {
|
|||||||
idleWakeupsPerSecond: number;
|
idleWakeupsPerSecond: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IDownloadManager {
|
||||||
|
_id: string;
|
||||||
|
fileName: string;
|
||||||
|
fileDisplayName: string;
|
||||||
|
savedPath: string;
|
||||||
|
total: number;
|
||||||
|
flashing?: boolean;
|
||||||
|
count?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IMediaPermission {
|
export interface IMediaPermission {
|
||||||
camera: string;
|
camera: string;
|
||||||
microphone: string;
|
microphone: string;
|
||||||
|
|||||||
@@ -95,6 +95,9 @@
|
|||||||
<div id="footer" class="hidden">
|
<div id="footer" class="hidden">
|
||||||
<div id="download-manager-footer" class="download-bar"></div>
|
<div id="download-manager-footer" class="download-bar"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<input type="button" id="open-download-item" value="Open Latest Item">
|
||||||
|
<input type="button" id="show-download-item" value="Show Latest Item in Finder">
|
||||||
|
<input type="button" id="close-download-manager" value="Close Download Manager">
|
||||||
<br>
|
<br>
|
||||||
<hr>
|
<hr>
|
||||||
<p>
|
<p>
|
||||||
@@ -211,6 +214,10 @@
|
|||||||
sendLogs: 'send-logs',
|
sendLogs: 'send-logs',
|
||||||
registerAnalyticHandler: 'register-analytic-handler',
|
registerAnalyticHandler: 'register-analytic-handler',
|
||||||
registerActivityDetection: 'register-activity-detection',
|
registerActivityDetection: 'register-activity-detection',
|
||||||
|
registerDownloadHandler: 'register-download-handler',
|
||||||
|
openDownloadItem: 'open-download-item',
|
||||||
|
showDownloadItem: 'show-download-item',
|
||||||
|
clearDownloadItems: 'clear-download-items',
|
||||||
showNotificationSettings: 'show-notification-settings',
|
showNotificationSettings: 'show-notification-settings',
|
||||||
sanitize: 'sanitize',
|
sanitize: 'sanitize',
|
||||||
bringToFront: 'bring-to-front',
|
bringToFront: 'bring-to-front',
|
||||||
@@ -539,6 +546,10 @@
|
|||||||
handleResponse(data);
|
handleResponse(data);
|
||||||
console.log(event.data);
|
console.log(event.data);
|
||||||
break;
|
break;
|
||||||
|
case 'download-handler-callback':
|
||||||
|
onDownload(data);
|
||||||
|
console.log(event.data);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
console.log(event.data);
|
console.log(event.data);
|
||||||
}
|
}
|
||||||
@@ -580,6 +591,21 @@
|
|||||||
console.log('bounds changed for=', arg)
|
console.log('bounds changed for=', arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (window.ssf) {
|
||||||
|
ssf.registerDownloadHandler(onDownload);
|
||||||
|
} else {
|
||||||
|
postMessage(apiCmds.registerDownloadHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDownload(data) {
|
||||||
|
if (data && data.status === 'download-completed') {
|
||||||
|
items = data.items;
|
||||||
|
console.log('Download completed!', data.items);
|
||||||
|
} else {
|
||||||
|
console.log('Download failed!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Protocol handler
|
* Protocol handler
|
||||||
*/
|
*/
|
||||||
@@ -804,6 +830,7 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let items = [];
|
||||||
/**
|
/**
|
||||||
* Download Manager api handler
|
* Download Manager api handler
|
||||||
*/
|
*/
|
||||||
@@ -831,7 +858,40 @@
|
|||||||
const filename = "bye.txt";
|
const filename = "bye.txt";
|
||||||
const text = document.getElementById("text-val").value;
|
const text = document.getElementById("text-val").value;
|
||||||
download(filename, text);
|
download(filename, text);
|
||||||
}, false);
|
}, false)
|
||||||
|
|
||||||
|
document.getElementById('open-download-item').addEventListener('click', () => {
|
||||||
|
if (!items || items.length < 1) {
|
||||||
|
alert('No files downloaded! Try again!');
|
||||||
|
}
|
||||||
|
const id = items[items.length - 1].id;
|
||||||
|
if (window.ssf) {
|
||||||
|
window.ssf.openDownloadItem(id);
|
||||||
|
} else {
|
||||||
|
postMessage(apiCmds.openDownloadItem, id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('show-download-item').addEventListener('click', () => {
|
||||||
|
if (!items || items.length < 1) {
|
||||||
|
alert('No files downloaded! Try again!');
|
||||||
|
}
|
||||||
|
const id = items[items.length - 1].id;
|
||||||
|
if (window.ssf) {
|
||||||
|
window.ssf.showDownloadItem(id);
|
||||||
|
} else {
|
||||||
|
postMessage(apiCmds.showDownloadItem, id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('close-download-manager').addEventListener('click', () => {
|
||||||
|
items = [];
|
||||||
|
if (window.ssf) {
|
||||||
|
window.ssf.clearDownloadItems();
|
||||||
|
} else {
|
||||||
|
postMessage(apiCmds.clearDownloadItems);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ export class AppBridge {
|
|||||||
onNotificationCallback: (event, data) => this.notificationCallback(event, data),
|
onNotificationCallback: (event, data) => this.notificationCallback(event, data),
|
||||||
onAnalyticsEventCallback: (data) => this.analyticsEventCallback(data),
|
onAnalyticsEventCallback: (data) => this.analyticsEventCallback(data),
|
||||||
restartFloater: (data) => this.restartFloater(data),
|
restartFloater: (data) => this.restartFloater(data),
|
||||||
|
onDownloadItemCallback: (data) => this.onDownloadItemCallback(data),
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -108,6 +109,19 @@ export class AppBridge {
|
|||||||
ssf.setBadgeCount(data as number);
|
ssf.setBadgeCount(data as number);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case apiCmds.openDownloadItem:
|
||||||
|
if (typeof data === 'string') {
|
||||||
|
ssf.openDownloadItem(data as string);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case apiCmds.showDownloadItem:
|
||||||
|
if (typeof data === 'string') {
|
||||||
|
ssf.showDownloadItem(data as string);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case apiCmds.clearDownloadItems:
|
||||||
|
ssf.clearDownloadItems();
|
||||||
|
break;
|
||||||
case apiCmds.setLocale:
|
case apiCmds.setLocale:
|
||||||
if (typeof data === 'string') {
|
if (typeof data === 'string') {
|
||||||
ssf.setLocale(data as string);
|
ssf.setLocale(data as string);
|
||||||
@@ -116,6 +130,9 @@ export class AppBridge {
|
|||||||
case apiCmds.registerActivityDetection:
|
case apiCmds.registerActivityDetection:
|
||||||
ssf.registerActivityDetection(data as number, this.callbackHandlers.onActivityCallback);
|
ssf.registerActivityDetection(data as number, this.callbackHandlers.onActivityCallback);
|
||||||
break;
|
break;
|
||||||
|
case apiCmds.registerDownloadHandler:
|
||||||
|
ssf.registerDownloadHandler(this.callbackHandlers.onDownloadItemCallback);
|
||||||
|
break;
|
||||||
case apiCmds.openScreenSnippet:
|
case apiCmds.openScreenSnippet:
|
||||||
ssf.openScreenSnippet(this.callbackHandlers.onScreenSnippetCallback);
|
ssf.openScreenSnippet(this.callbackHandlers.onScreenSnippetCallback);
|
||||||
break;
|
break;
|
||||||
@@ -243,6 +260,14 @@ export class AppBridge {
|
|||||||
this.broadcastMessage('analytics-event-callback', arg);
|
this.broadcastMessage('analytics-event-callback', arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcast download item event
|
||||||
|
* @param arg {object}
|
||||||
|
*/
|
||||||
|
private onDownloadItemCallback(arg: object): void {
|
||||||
|
this.broadcastMessage('download-handler-callback', arg);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Broadcast to restart floater event with data
|
* Broadcast to restart floater event with data
|
||||||
* @param arg {IAnalyticsData}
|
* @param arg {IAnalyticsData}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ interface IDownloadManager {
|
|||||||
_id: string;
|
_id: string;
|
||||||
fileName: string;
|
fileName: string;
|
||||||
savedPath: string;
|
savedPath: string;
|
||||||
total: number;
|
total: string;
|
||||||
flashing: boolean;
|
flashing: boolean;
|
||||||
count: number;
|
count: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ if (ssfWindow.ssf) {
|
|||||||
bringToFront: ssfWindow.ssf.bringToFront,
|
bringToFront: ssfWindow.ssf.bringToFront,
|
||||||
getVersionInfo: ssfWindow.ssf.getVersionInfo,
|
getVersionInfo: ssfWindow.ssf.getVersionInfo,
|
||||||
registerActivityDetection: ssfWindow.ssf.registerActivityDetection,
|
registerActivityDetection: ssfWindow.ssf.registerActivityDetection,
|
||||||
|
registerDownloadHandler: ssfWindow.ssf.registerDownloadHandler,
|
||||||
registerBoundsChange: ssfWindow.ssf.registerBoundsChange,
|
registerBoundsChange: ssfWindow.ssf.registerBoundsChange,
|
||||||
registerLogger: ssfWindow.ssf.registerLogger,
|
registerLogger: ssfWindow.ssf.registerLogger,
|
||||||
registerProtocolHandler: ssfWindow.ssf.registerProtocolHandler,
|
registerProtocolHandler: ssfWindow.ssf.registerProtocolHandler,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
apiName,
|
apiName,
|
||||||
IBadgeCount,
|
IBadgeCount,
|
||||||
IBoundsChange,
|
IBoundsChange,
|
||||||
ICPUUsage,
|
ICPUUsage, IDownloadManager,
|
||||||
ILogMsg,
|
ILogMsg,
|
||||||
IMediaPermission,
|
IMediaPermission,
|
||||||
IRestartFloaterData,
|
IRestartFloaterData,
|
||||||
@@ -36,6 +36,7 @@ export interface ILocalObject {
|
|||||||
ipcRenderer;
|
ipcRenderer;
|
||||||
logger?: (msg: ILogMsg, logLevel: LogLevel, showInConsole: boolean) => void;
|
logger?: (msg: ILogMsg, logLevel: LogLevel, showInConsole: boolean) => void;
|
||||||
activityDetectionCallback?: (arg: number) => void;
|
activityDetectionCallback?: (arg: number) => void;
|
||||||
|
downloadManagerCallback?: (arg?: any) => void;
|
||||||
screenSnippetCallback?: (arg: IScreenSnippet) => void;
|
screenSnippetCallback?: (arg: IScreenSnippet) => void;
|
||||||
boundsChangeCallback?: (arg: IBoundsChange) => void;
|
boundsChangeCallback?: (arg: IBoundsChange) => void;
|
||||||
screenSharingIndicatorCallback?: (arg: IScreenSharingIndicator) => void;
|
screenSharingIndicatorCallback?: (arg: IScreenSharingIndicator) => void;
|
||||||
@@ -101,6 +102,26 @@ const throttledSetCloudConfig = throttle((data) => {
|
|||||||
});
|
});
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
|
const throttledOpenDownloadItem = throttle((id: string) => {
|
||||||
|
ipcRenderer.send(apiName.symphonyApi, {
|
||||||
|
cmd: apiCmds.openDownloadItem,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
const throttledShowDownloadItem = throttle((id: string) => {
|
||||||
|
ipcRenderer.send(apiName.symphonyApi, {
|
||||||
|
cmd: apiCmds.showDownloadItem,
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
const throttledClearDownloadItems = throttle(() => {
|
||||||
|
ipcRenderer.send(apiName.symphonyApi, {
|
||||||
|
cmd: apiCmds.clearDownloadItems,
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
let cryptoLib: ICryptoLib | null;
|
let cryptoLib: ICryptoLib | null;
|
||||||
try {
|
try {
|
||||||
cryptoLib = remote.require('../app/crypto-handler.js').cryptoLibrary;
|
cryptoLib = remote.require('../app/crypto-handler.js').cryptoLibrary;
|
||||||
@@ -215,6 +236,20 @@ export class SSFApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the download handler
|
||||||
|
* @param downloadManagerCallback Callback to be triggered by the download handler
|
||||||
|
*/
|
||||||
|
public registerDownloadHandler(downloadManagerCallback: (arg: any) => void): void {
|
||||||
|
if (typeof downloadManagerCallback === 'function') {
|
||||||
|
local.downloadManagerCallback = downloadManagerCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
local.ipcRenderer.send(apiName.symphonyApi, {
|
||||||
|
cmd: apiCmds.registerDownloadHandler,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows JS to register a callback to be invoked when size/positions
|
* Allows JS to register a callback to be invoked when size/positions
|
||||||
* changes for any pop-out window (i.e., window.open). The main
|
* changes for any pop-out window (i.e., window.open). The main
|
||||||
@@ -487,6 +522,29 @@ export class SSFApi {
|
|||||||
throttledSetCloudConfig(data);
|
throttledSetCloudConfig(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open Downloaded item
|
||||||
|
* @param id ID of the item
|
||||||
|
*/
|
||||||
|
public openDownloadItem(id: string): void {
|
||||||
|
throttledOpenDownloadItem(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show downloaded item in finder / explorer
|
||||||
|
* @param id ID of the item
|
||||||
|
*/
|
||||||
|
public showDownloadItem(id: string): void {
|
||||||
|
throttledShowDownloadItem(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears downloaded items
|
||||||
|
*/
|
||||||
|
public clearDownloadItems(): void {
|
||||||
|
throttledClearDownloadItems();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get CPU usage
|
* get CPU usage
|
||||||
*/
|
*/
|
||||||
@@ -596,6 +654,18 @@ local.ipcRenderer.on('activity', (_event: Event, idleTime: number) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
local.ipcRenderer.on('download-completed', (_event: Event, downloadItems: IDownloadManager[]) => {
|
||||||
|
if (typeof downloadItems === 'object' && typeof local.downloadManagerCallback === 'function') {
|
||||||
|
local.downloadManagerCallback({status: 'download-completed', items: downloadItems});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
local.ipcRenderer.on('download-failed', (_event: Event) => {
|
||||||
|
if (typeof local.downloadManagerCallback === 'function') {
|
||||||
|
local.downloadManagerCallback({status: 'download-failed'});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An event triggered by the main process
|
* An event triggered by the main process
|
||||||
* Whenever some Window position or dimension changes
|
* Whenever some Window position or dimension changes
|
||||||
|
|||||||
Reference in New Issue
Block a user