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
This commit is contained in:
Vishwas Shashidhar 2019-07-19 18:48:01 +05:30 committed by GitHub
parent f8ea6a778f
commit e1f7fa53d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 94 additions and 68 deletions

View File

@ -42,6 +42,7 @@ jest.mock('../src/app/window-handler', () => {
createScreenPickerWindow: jest.fn(),
createScreenSharingIndicatorWindow: jest.fn(),
isOnline: false,
updateVersionInfo: jest.fn(),
},
};
});

View File

@ -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') {

View File

@ -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();

View File

@ -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<IVersionInfo> {
public getClientVersion(fetchFromServer: boolean = false, mainUrl?: string): Promise<IVersionInfo> {
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;

View File

@ -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};
}
}