From e1f7fa53d035aadc4d74edcad873198ce7c18b9b Mon Sep 17 00:00:00 2001 From: Vishwas Shashidhar Date: Fri, 19 Jul 2019 18:48:01 +0530 Subject: [PATCH] fix: ELECTRON-1431: add logic to update pod version dynamically (#743) * ELECTRON-1431: add logic to update pod version dynamically * ELECTRON-1431: refactor the code to keep version handler simple * ELECTRON-1431: add safety check for setting about panel on macOS * Merge branch 'master' into ELECTRON-1431 # Conflicts: # src/app/window-handler.ts --- spec/mainApiHandler.spec.ts | 1 + src/app/main-api-handler.ts | 3 + src/app/main.ts | 18 ------ src/app/version-handler.ts | 31 ++++++---- src/app/window-handler.ts | 109 +++++++++++++++++++++++------------- 5 files changed, 94 insertions(+), 68 deletions(-) diff --git a/spec/mainApiHandler.spec.ts b/spec/mainApiHandler.spec.ts index b31c1e28..f21bc968 100644 --- a/spec/mainApiHandler.spec.ts +++ b/spec/mainApiHandler.spec.ts @@ -42,6 +42,7 @@ jest.mock('../src/app/window-handler', () => { createScreenPickerWindow: jest.fn(), createScreenSharingIndicatorWindow: jest.fn(), isOnline: false, + updateVersionInfo: jest.fn(), }, }; }); diff --git a/src/app/main-api-handler.ts b/src/app/main-api-handler.ts index d294c8c6..47c36a98 100644 --- a/src/app/main-api-handler.ts +++ b/src/app/main-api-handler.ts @@ -49,6 +49,9 @@ ipcMain.on(apiName.symphonyApi, (event: Electron.Event, arg: IApiArgs) => { break; case apiCmds.registerProtocolHandler: protocolHandler.setPreloadWebContents(event.sender); + // Since we register the prococol handler window upon login, + // we make use of it and update the pod version info on SDA + windowHandler.updateVersionInfo(); break; case apiCmds.badgeDataUrl: if (typeof arg.dataUrl === 'string' && typeof arg.count === 'number') { diff --git a/src/app/main.ts b/src/app/main.ts index 3f734f21..cba51da4 100644 --- a/src/app/main.ts +++ b/src/app/main.ts @@ -13,7 +13,6 @@ import './dialog-handler'; import './main-api-handler'; import { handlePerformanceSettings } from './perf-handler'; import { protocolHandler } from './protocol-handler'; -import { IVersionInfo, versionHandler } from './version-handler'; import { ICustomBrowserWindow, windowHandler } from './window-handler'; logger.info(`App started with the args ${JSON.stringify(process.argv)}`); @@ -53,28 +52,11 @@ setChromeFlags(); // Electron sets the default protocol app.setAsDefaultProtocolClient('symphony'); -const setAboutPanel = (clientVersion: string, buildNumber: string) => { - const appName = app.getName(); - const copyright = `Copyright \xA9 ${new Date().getFullYear()} ${appName}`; - app.setAboutPanelOptions({ - applicationName: appName, - applicationVersion: clientVersion, - version: buildNumber, - copyright, - }); -}; - /** * Main function that init the application */ const startApplication = async () => { await app.whenReady(); - versionHandler.getClientVersion() - .then((versionInfo: IVersionInfo) => { - if (isMac) { - setAboutPanel(versionInfo.clientVersion, versionInfo.buildNumber); - } - }); logger.info(`main: app is ready, performing initial checks`); createAppCacheFile(); windowHandler.createApplication(); diff --git a/src/app/version-handler.ts b/src/app/version-handler.ts index 29765c09..b30e198f 100644 --- a/src/app/version-handler.ts +++ b/src/app/version-handler.ts @@ -19,13 +19,14 @@ interface IVersionInfo { aresVersion: string; httpParserVersion: string; swiftSearchVersion: string; - swiftSerchSupportedVersion: string; + swiftSearchSupportedVersion: string; } class VersionHandler { - private versionInfo: IVersionInfo; + public versionInfo: IVersionInfo; private serverVersionInfo: any; + private mainUrl; constructor() { this.versionInfo = { @@ -43,32 +44,42 @@ class VersionHandler { aresVersion: process.versions.ares, httpParserVersion: process.versions.http_parser, swiftSearchVersion: optionalDependencies['swift-search'], - swiftSerchSupportedVersion: searchAPIVersion, + swiftSearchSupportedVersion: searchAPIVersion, }; + this.mainUrl = null; } /** * Get Symphony version from the pod */ - public getClientVersion(): Promise { + public getClientVersion(fetchFromServer: boolean = false, mainUrl?: string): Promise { return new Promise((resolve) => { - if (this.serverVersionInfo) { + + if (this.serverVersionInfo && !fetchFromServer) { this.versionInfo.clientVersion = this.serverVersionInfo['Implementation-Version'] || this.versionInfo.clientVersion; this.versionInfo.buildNumber = this.serverVersionInfo['Implementation-Build'] || this.versionInfo.buildNumber; resolve(this.versionInfo); return; } + if (mainUrl) { + this.mainUrl = mainUrl; + } + const { url: podUrl }: IConfig = config.getGlobalConfigFields(['url']); - if (!podUrl) { + if (!this.mainUrl || !nodeURL.parse(this.mainUrl)) { + this.mainUrl = podUrl; + } + + if (!this.mainUrl) { logger.error(`version-handler: Unable to get pod url for getting version data from server! Setting defaults!`); resolve(this.versionInfo); return; } - const hostname = nodeURL.parse(podUrl).hostname; - const protocol = nodeURL.parse(podUrl).protocol; + const hostname = nodeURL.parse(this.mainUrl).hostname; + const protocol = nodeURL.parse(this.mainUrl).protocol; const versionApiPath = '/webcontroller/HealthCheck/version/advanced'; const url = `${protocol}//${hostname}${versionApiPath}`; @@ -96,7 +107,7 @@ class VersionHandler { } }); - res.on('error', (error) => { + res.on('error', (error: Error) => { logger.error(`version-handler: Error getting version data from the server! ${error}`); resolve(this.versionInfo); return; @@ -104,7 +115,7 @@ class VersionHandler { }); - request.on('error', (error) => { + request.on('error', (error: Error) => { logger.error(`version-handler: Error getting version data from the server! ${error}`); resolve(this.versionInfo); return; diff --git a/src/app/window-handler.ts b/src/app/window-handler.ts index e1016789..6c7efd34 100644 --- a/src/app/window-handler.ts +++ b/src/app/window-handler.ts @@ -16,7 +16,7 @@ import { config, IConfig } from './config-handler'; import { SpellChecker } from './spell-check-handler'; import { checkIfBuildExpired } from './ttl-handler'; import DesktopCapturerSource = Electron.DesktopCapturerSource; -import { IVersionInfo, versionHandler } from './version-handler'; +import { versionHandler } from './version-handler'; import { handlePermissionRequests, monitorWindowActions } from './window-actions'; import { createComponentWindow, @@ -88,9 +88,9 @@ export class WindowHandler { constructor(opts?: Electron.BrowserViewConstructorOptions) { // Use these variables only on initial setup - this.config = config.getConfigFields([ 'isCustomTitleBar', 'mainWinPos', 'minimizeOnClose', 'notificationSettings', 'alwaysOnTop' ]); - this.globalConfig = config.getGlobalConfigFields([ 'url', 'contextIsolation', 'customFlags' ]); - const { url, contextIsolation, customFlags }: IConfig = this.globalConfig; + this.config = config.getConfigFields(['isCustomTitleBar', 'mainWinPos', 'minimizeOnClose', 'notificationSettings', 'alwaysOnTop']); + this.globalConfig = config.getGlobalConfigFields(['url', 'contextIsolation', 'customFlags']); + const {url, contextIsolation, customFlags}: IConfig = this.globalConfig; this.windows = {}; this.contextIsolation = contextIsolation || false; @@ -114,12 +114,13 @@ export class WindowHandler { this.appMenu = null; try { - const extra = { podUrl: url, process: 'main' }; - const defaultOpts = { uploadToServer: false, companyName: 'Symphony', submitURL: '' }; - crashReporter.start({ ...defaultOpts, extra }); + const extra = {podUrl: url, process: 'main'}; + const defaultOpts = {uploadToServer: false, companyName: 'Symphony', submitURL: ''}; + crashReporter.start({...defaultOpts, extra}); } catch (e) { throw new Error('failed to init crash report'); } + } /** @@ -127,6 +128,7 @@ export class WindowHandler { */ public createApplication() { + this.updateVersionInfo(); this.spellchecker = new SpellChecker(); logger.info(`window-handler: initialized spellchecker module with locale ${this.spellchecker.locale}`); @@ -135,7 +137,7 @@ export class WindowHandler { ...this.windowOpts, ...getBounds(this.config.mainWinPos, DEFAULT_WIDTH, DEFAULT_HEIGHT), }) as ICustomBrowserWindow; this.mainWindow.winName = apiName.mainWindowName; - const { isFullScreen, isMaximized } = this.config.mainWinPos ? this.config.mainWinPos : { isFullScreen: false, isMaximized: false }; + const {isFullScreen, isMaximized} = this.config.mainWinPos ? this.config.mainWinPos : {isFullScreen: false, isMaximized: false}; if (isMaximized) { this.mainWindow.maximize(); logger.info(`window-handler: window is maximized!`); @@ -183,7 +185,7 @@ export class WindowHandler { isMainWindow: true, }); this.appMenu = new AppMenu(); - const { permissions } = config.getGlobalConfigFields([ 'permissions' ]); + const {permissions} = config.getGlobalConfigFields(['permissions']); this.mainWindow.webContents.send('is-screen-share-enabled', permissions.media); }); @@ -199,7 +201,7 @@ export class WindowHandler { if (href === 'data:text/html,chromewebdata' || href === 'chrome-error://chromewebdata/') { if (this.mainWindow && windowExists(this.mainWindow)) { this.mainWindow.webContents.insertCSS(fs.readFileSync(path.join(__dirname, '..', '/renderer/styles/network-error.css'), 'utf8').toString()); - this.mainWindow.webContents.send('network-error', { error: this.loadFailError }); + this.mainWindow.webContents.send('network-error', {error: this.loadFailError}); isSymphonyReachable(this.mainWindow); } } @@ -219,7 +221,7 @@ export class WindowHandler { type: 'error', title: i18n.t('Renderer Process Crashed')(), message: i18n.t('Oops! Looks like we have had a crash. Please reload or close this window.')(), - buttons: [ 'Reload', 'Close' ], + buttons: ['Reload', 'Close'], }, (index: number) => { if (!this.mainWindow || !windowExists(this.mainWindow)) { return; @@ -239,7 +241,7 @@ export class WindowHandler { return this.destroyAllWindows(); } - const { minimizeOnClose } = config.getConfigFields([ 'minimizeOnClose' ]); + const {minimizeOnClose} = config.getConfigFields(['minimizeOnClose']); if (minimizeOnClose) { event.preventDefault(); isMac ? this.mainWindow.hide() : this.mainWindow.minimize(); @@ -321,7 +323,7 @@ export class WindowHandler { break; case 'screen-sharing-indicator': if (winKey) { - const browserWindow = this.windows[ winKey ]; + const browserWindow = this.windows[winKey]; if (browserWindow && windowExists(browserWindow)) { browserWindow.close(); } @@ -373,7 +375,7 @@ export class WindowHandler { * @param window {Electron.BrowserWindow} */ public hasWindow(key: string, window: Electron.BrowserWindow): boolean { - const browserWindow = this.windows[ key ]; + const browserWindow = this.windows[key]; return browserWindow && window === browserWindow; } @@ -414,13 +416,16 @@ export class WindowHandler { this.aboutAppWindow = createComponentWindow('about-app', opts); this.aboutAppWindow.setVisibleOnAllWorkspaces(true); this.aboutAppWindow.webContents.once('did-finish-load', async () => { - if (!this.aboutAppWindow || !windowExists(this.aboutAppWindow)) { - return; - } const ABOUT_SYMPHONY_NAMESPACE = 'AboutSymphony'; const versionLocalised = i18n.t('Version', ABOUT_SYMPHONY_NAMESPACE)(); - const { clientVersion, buildNumber }: IVersionInfo = await versionHandler.getClientVersion(); - this.aboutAppWindow.webContents.send('about-app-data', { buildNumber, clientVersion, versionLocalised }); + const aboutInfo = { + buildNumber: versionHandler.versionInfo.buildNumber, + clientVersion: versionHandler.versionInfo.clientVersion, + versionLocalised, + }; + if (this.aboutAppWindow && windowExists(this.aboutAppWindow)) { + this.aboutAppWindow.webContents.send('about-app-data', aboutInfo); + } }); } @@ -448,11 +453,9 @@ export class WindowHandler { this.moreInfoWindow = createComponentWindow('more-info', opts); this.moreInfoWindow.webContents.once('did-finish-load', async () => { - if (!this.moreInfoWindow || !windowExists(this.moreInfoWindow)) { - return; + if (this.moreInfoWindow && windowExists(this.moreInfoWindow)) { + this.moreInfoWindow.webContents.send('more-info-data', versionHandler.versionInfo); } - const versionInfo: IVersionInfo = await versionHandler.getClientVersion(); - this.moreInfoWindow.webContents.send('more-info-data', versionInfo); }); } @@ -490,7 +493,7 @@ export class WindowHandler { if (!this.screenPickerWindow || !windowExists(this.screenPickerWindow)) { return; } - this.screenPickerWindow.webContents.send('screen-picker-data', { sources, id }); + this.screenPickerWindow.webContents.send('screen-picker-data', {sources, id}); this.addWindow(opts.winKey, this.screenPickerWindow); }); ipcMain.once('screen-source-selected', (_event, source) => { @@ -535,7 +538,7 @@ export class WindowHandler { if (!this.basicAuthWindow || !windowExists(this.basicAuthWindow)) { return; } - this.basicAuthWindow.webContents.send('basic-auth-data', { hostname, isValidCredentials: isMultipleTries }); + this.basicAuthWindow.webContents.send('basic-auth-data', {hostname, isValidCredentials: isMultipleTries}); }); const closeBasicAuth = (shouldClearSettings = true) => { if (shouldClearSettings) { @@ -548,7 +551,7 @@ export class WindowHandler { }; const login = (_event, arg) => { - const { username, password } = arg; + const {username, password} = arg; callback(username, password); closeBasicAuth(false); }; @@ -606,17 +609,17 @@ export class WindowHandler { if (app.isReady()) { screens = electron.screen.getAllDisplays(); } - const { position, display } = config.getConfigFields([ 'notificationSettings' ]).notificationSettings; - this.notificationSettingsWindow.webContents.send('notification-settings-data', { screens, position, display }); + const {position, display} = config.getConfigFields(['notificationSettings']).notificationSettings; + this.notificationSettingsWindow.webContents.send('notification-settings-data', {screens, position, display}); } }); this.addWindow(opts.winKey, this.notificationSettingsWindow); ipcMain.once('notification-settings-update', async (_event, args) => { - const { display, position } = args; + const {display, position} = args; try { - await config.updateUserConfig({ notificationSettings: { display, position } }); + await config.updateUserConfig({notificationSettings: {display, position}}); } catch (e) { logger.error(`NotificationSettings: Could not update user config file error`, e); } @@ -650,7 +653,7 @@ export class WindowHandler { ): void { const indicatorScreen = (displayId && electron.screen.getAllDisplays().filter((d) => - displayId.includes(d.id.toString()))[ 0 ]) || electron.screen.getPrimaryDisplay(); + displayId.includes(d.id.toString()))[0]) || electron.screen.getPrimaryDisplay(); const screenRect = indicatorScreen.workArea; // Set stream id as winKey to link stream to the window @@ -669,7 +672,7 @@ export class WindowHandler { fullscreenable: false, }, { devTools: false, - }), ...{ winKey: streamId }, + }), ...{winKey: streamId}, }; if (opts.width && opts.height) { opts = Object.assign({}, opts, { @@ -683,7 +686,7 @@ export class WindowHandler { if (!this.screenSharingIndicatorWindow || !windowExists(this.screenSharingIndicatorWindow)) { return; } - this.screenSharingIndicatorWindow.webContents.send('screen-sharing-indicator-data', { id, streamId }); + this.screenSharingIndicatorWindow.webContents.send('screen-sharing-indicator-data', {id, streamId}); }); const stopScreenSharing = (_event, indicatorId) => { if (id === indicatorId) { @@ -701,6 +704,14 @@ export class WindowHandler { ipcMain.once('stop-screen-sharing', stopScreenSharing); } + /** + * Update version info on the about app window and more info window + */ + public async updateVersionInfo() { + await versionHandler.getClientVersion(true, this.url); + this.setAboutPanel(); + } + /** * Opens an external url in the system's default browser * @@ -720,7 +731,7 @@ export class WindowHandler { * @param browserWindow {Electron.BrowserWindow} */ public addWindow(key: string, browserWindow: Electron.BrowserWindow): void { - this.windows[ key ] = browserWindow; + this.windows[key] = browserWindow; } /** @@ -729,7 +740,25 @@ export class WindowHandler { * @param key {string} */ public removeWindow(key: string): void { - delete this.windows[ key ]; + delete this.windows[key]; + } + + /** + * Sets the about panel details for macOS + */ + private setAboutPanel() { + if (!isMac) { + return; + } + const appName = app.getName(); + const copyright = `Copyright \xA9 ${new Date().getFullYear()} ${appName}`; + app.setAboutPanelOptions({ + applicationName: appName, + applicationVersion: versionHandler.versionInfo.clientVersion, + version: versionHandler.versionInfo.buildNumber, + copyright, + }); + } /** @@ -754,7 +783,7 @@ export class WindowHandler { */ private onRegisterDevtools(): void { const focusedWindow = BrowserWindow.getFocusedWindow(); - const { devToolsEnabled } = config.getGlobalConfigFields([ 'devToolsEnabled' ]); + const {devToolsEnabled} = config.getGlobalConfigFields(['devToolsEnabled']); if (!focusedWindow || !windowExists(focusedWindow)) { return; } @@ -766,7 +795,7 @@ export class WindowHandler { logger.info(`window-handler: dev tools disabled by admin, showing error dialog to user!`); electron.dialog.showMessageBox(focusedWindow, { type: 'warning', - buttons: [ 'Ok' ], + buttons: ['Ok'], title: i18n.t('Dev Tools disabled')(), message: i18n.t('Dev Tools has been disabled! Please contact your system administrator to enable it!')(), }); @@ -778,7 +807,7 @@ export class WindowHandler { private destroyAllWindows(): void { for (const key in this.windows) { if (Object.prototype.hasOwnProperty.call(this.windows, key)) { - const winKey = this.windows[ key ]; + const winKey = this.windows[key]; this.removeWindow(winKey); } } @@ -807,7 +836,7 @@ export class WindowHandler { type: 'error', title: i18n.t('Build expired')(), message: i18n.t('Sorry, this is a test build and it has expired. Please contact your administrator to get a production build.')(), - buttons: [ i18n.t('Quit')() ], + buttons: [i18n.t('Quit')()], cancelId: 0, }; @@ -835,7 +864,7 @@ export class WindowHandler { winKey: getGuid(), }; - return { ...defaultWindowOpts, ...windowOpts }; + return {...defaultWindowOpts, ...windowOpts}; } }