Typescript - Optimize protocol handler

This commit is contained in:
Kiran Niranjan 2019-01-21 14:18:48 +05:30
parent 152839972e
commit 36213a6a18
4 changed files with 87 additions and 116 deletions

View File

@ -5,7 +5,7 @@ import { LocaleType } from '../common/i18n';
import { logger } from '../common/logger';
import { activityDetection } from './activity-detection';
import { config } from './config-handler';
import { checkProtocolAction, setProtocolWindow } from './protocol-handler';
import { protocolHandler } from './protocol-handler';
import { screenSnippet } from './screen-snippet-handler';
import { activate, handleKeyPress } from './window-actions';
import { windowHandler } from './window-handler';
@ -44,8 +44,7 @@ ipcMain.on(apiName.symphonyApi, (event: Electron.Event, arg: IApiArgs) => {
}
break;
case apiCmds.registerProtocolHandler:
setProtocolWindow(event.sender);
checkProtocolAction();
protocolHandler.setPreloadWebContents(event.sender);
break;
case apiCmds.badgeDataUrl:
if (typeof arg.dataUrl === 'string' && typeof arg.count === 'number') {

View File

@ -1,5 +1,6 @@
import { app } from 'electron';
import { buildNumber, clientVersion, version } from '../../package.json';
import { isDevEnv, isMac } from '../common/env';
import { logger } from '../common/logger';
import { getCommandLineArgs } from '../common/utils';
@ -9,11 +10,20 @@ import { setChromeFlags } from './chrome-flags';
import { config } from './config-handler';
import './dialog-handler';
import './main-api-handler';
import { protocolHandler } from './protocol-handler';
import { SpellChecker } from './spell-check-handler';
import { ICustomBrowserWindow, windowHandler } from './window-handler';
const allowMultiInstance: string | boolean = getCommandLineArgs(process.argv, '--multiInstance', true) || isDevEnv;
// on windows, we create the protocol handler via the installer
// because electron leaves registry traces upon uninstallation
if (isMac) {
app.setAsDefaultProtocolClient('symphony');
// Sets application version info that will be displayed in about app panel
app.setAboutPanelOptions({ applicationVersion: `${clientVersion}-${version}`, version: buildNumber });
}
/**
* Main function that init the application
*/
@ -52,7 +62,7 @@ if (!allowMultiInstance) {
app.quit();
} else {
logger.info('Creating the first instance of the application');
app.on('second-instance', (_event) => {
app.on('second-instance', (_event, argv) => {
// Someone tried to run a second instance, we should focus our window.
const mainWindow = windowHandler.getMainWindow();
if (mainWindow && !mainWindow.isDestroyed()) {
@ -61,7 +71,7 @@ if (!allowMultiInstance) {
mainWindow.restore();
}
mainWindow.focus();
// TODO: Handle protocol action
protocolHandler.processArgv(argv);
}
});
startApplication();
@ -87,6 +97,12 @@ app.on('quit', () => cleanUpAppCache());
*/
app.on('before-quit', () => windowHandler.willQuitApp = true);
/**
* Is triggered when the application is launched
* or clicking the application's dock or taskbar icon
*
* This event is emitted only on macOS at this moment
*/
app.on('activate', () => {
const mainWindow: ICustomBrowserWindow | null = windowHandler.getMainWindow();
if (!mainWindow || mainWindow.isDestroyed()) {
@ -94,4 +110,11 @@ app.on('activate', () => {
} else {
mainWindow.show();
}
});
});
/**
* Validates and Sends protocol action
*
* This event is emitted only on macOS at this moment
*/
app.on('open-url', (_event, url) => protocolHandler.sendProtocol(url));

View File

@ -1,127 +1,76 @@
import * as url from 'url';
import { isMac } from '../common/env';
import { logger } from '../common/logger';
import { apiName } from '../common/api-interface';
import { isMac, isWindowsOS } from '../common/env';
import { getCommandLineArgs } from '../common/utils';
import { windowHandler } from './window-handler';
import { activate } from './window-actions';
let protocolWindow: Electron.WebContents;
let protocolUrl: string | undefined;
enum protocol {
SymphonyProtocol = 'symphony://',
}
/**
* Caches the protocol uri
* @param {String} uri - the uri opened in the format 'symphony://...'
*/
const setProtocolUrl = (uri: string): void => {
logger.info(`Setting the property protocol url to ${uri}`);
protocolUrl = uri;
};
class ProtocolHandler {
/**
* Processes a protocol uri
* @param {String} uri - the uri opened in the format 'symphony://abc?def=ghi'
*/
export const processProtocolUri = (uri: string): void => {
private static isValidProtocolUri = (uri: string): boolean => !!(uri && uri.startsWith(protocol.SymphonyProtocol));
logger.info(`Processing protocol action, uri ${uri}`);
if (!protocolWindow) {
logger.info(`protocol window not yet initialized, caching the uri ${uri}`);
setProtocolUrl(uri);
return;
private preloadWebContents: Electron.WebContents | null = null;
private protocolUri: string | null = null;
constructor() {
this.processArgv();
}
if (uri && uri.startsWith('symphony://')) {
logger.info(`triggering the protocol action for the uri ${uri}`);
protocolWindow.send('protocol-action', uri);
}
};
/**
* Handles a protocol action based on the current state of the app
* @param uri
* @param isAppAlreadyOpen {Boolean} whether the app is already open
*/
export const handleProtocolAction = (uri: string, isAppAlreadyOpen: boolean): void => {
if (!isAppAlreadyOpen) {
logger.info(`App started by protocol url ${uri}. We are caching this to be processed later!`);
// app is opened by the protocol url, cache the protocol url to be used later
setProtocolUrl(uri);
return;
}
// This is needed for mac OS as it brings pop-outs to foreground
// (if it has been previously focused) instead of main window
if (isMac) {
logger.info('Bringing the main window to foreground for focus and processing the protocol url (macOS)');
const mainWindow = windowHandler.getMainWindow();
if (mainWindow && !mainWindow.isDestroyed()) {
// windowMgr.activate(mainWindow.winName);
/**
* Stores the web contents of the preload
*
* @param webContents {Electron.WebContents}
*/
public setPreloadWebContents(webContents: Electron.WebContents): void {
this.preloadWebContents = webContents;
// check for cashed protocol uri and process it
if (this.protocolUri) {
this.sendProtocol(this.protocolUri);
this.protocolUri = null;
}
}
// app is already open, so, just trigger the protocol action method
logger.info(`App opened by protocol url ${uri}`);
processProtocolUri(uri);
};
/**
* Processes protocol action for windows clients
* @param argv {Array} an array of command line arguments
* @param isAppAlreadyOpen {Boolean} whether the app is already open
*/
export const processProtocolArgv = (argv: string[], isAppAlreadyOpen: boolean): void => {
// In case of windows, we need to handle protocol handler
// manually because electron doesn't emit
// 'open-url' event on windows
if (!(process.platform === 'win32')) {
logger.info('This is windows, not processing protocol url through arguments');
return;
}
const protocolUri = getCommandLineArgs(argv, 'symphony://', false);
logger.info(`Trying to process a protocol action for uri ${protocolUri}`);
if (protocolUri) {
const parsedURL = url.parse(protocolUri);
if (!parsedURL.protocol || !parsedURL.slashes) {
/**
* Sends the protocol uri to the web app to further process
*
* @param uri {String}
* @param isAppRunning {Boolean} - whether the application is running
*/
public sendProtocol(uri: string, isAppRunning: boolean = true): void {
if (!this.preloadWebContents || !isAppRunning) {
this.protocolUri = uri;
return;
}
logger.info(`Successfully parsed protocol url for ${parsedURL}`);
handleProtocolAction(protocolUri, isAppAlreadyOpen);
// This is needed for mac OS as it brings pop-outs to foreground
// (if it has been previously focused) instead of main window
if (isMac) activate(apiName.mainWindowName);
if (ProtocolHandler.isValidProtocolUri(uri)) {
this.preloadWebContents.send('protocol-action', uri);
}
}
};
/**
* Sets the protocol window
* @param {Object} win - the renderer window
*/
export const setProtocolWindow = (win: Electron.WebContents): void => {
logger.info(`Setting protocol window ${win}`);
protocolWindow = win;
};
/**
* Checks to see if the app was opened by a uri
*/
export const checkProtocolAction = (): void => {
logger.info('Checking if we have a cached protocol url');
if (protocolUrl) {
logger.info(`Found a cached protocol url (${protocolUrl}), processing it`);
processProtocolUri(protocolUrl);
logger.info('Resetting the protocol url to undefined post processing it');
protocolUrl = undefined;
/**
* Handles protocol uri from process.argv
*
* @param argv {String[]} - data received from process.argv
*/
public processArgv(argv?: string[]): void {
const protocolUriFromArgv = getCommandLineArgs(argv || process.argv, protocol.SymphonyProtocol, false);
if (isWindowsOS && protocolUriFromArgv) {
const parsedURL = url.parse(protocolUriFromArgv);
if (!parsedURL.protocol || !parsedURL.slashes) {
return;
}
this.sendProtocol(protocolUriFromArgv, false);
}
}
};
}
/**
* Gets the protocol url set against an instance
* @returns {*}
*/
export const getProtocolUrl = (): string | undefined => {
logger.info(`Getting the property protocol url ${protocolUrl}`);
return protocolUrl;
};
const protocolHandler = new ProtocolHandler();
export { protocolHandler };

View File

@ -22,7 +22,7 @@ interface IClientLogMsg {
const MAX_LOG_QUEUE_LENGTH = 100;
export class Logger {
class Logger {
private readonly showInConsole: boolean = false;
private readonly desiredLogLevel?: LogLevel;
private readonly logQueue: ILogMsg[];