feat: SDA-2559: clean app cache on install & crash (#1091)

* SDA-2559: clean app cache on install

- Clean app cache on a fresh install of SDA to avoid upgradation problems
- Clean app cache and restart SDA when it crashes or is unresponsive

* SDA-2559: fix failing unit test

* SDA-2559: add dialog before restarting the app

* SDA-2559: add translations

* SDA-2559: fix failing unit tests and refactor code
This commit is contained in:
Vishwas Shashidhar 2020-10-14 17:28:28 +05:30 committed by GitHub
parent c13a6a16ad
commit e3720c22f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 185 additions and 65 deletions

View File

@ -118,6 +118,7 @@
"@types/react": "16.8.3", "@types/react": "16.8.3",
"@types/react-dom": "16.0.9", "@types/react-dom": "16.0.9",
"@types/ref-napi": "1.4.0", "@types/ref-napi": "1.4.0",
"@types/rimraf": "^3.0.0",
"ava": "2.4.0", "ava": "2.4.0",
"browserify": "16.5.1", "browserify": "16.5.1",
"cross-env": "5.2.0", "cross-env": "5.2.0",
@ -161,6 +162,7 @@
"react": "16.13.0", "react": "16.13.0",
"react-dom": "16.13.0", "react-dom": "16.13.0",
"ref-napi": "1.4.3", "ref-napi": "1.4.3",
"rimraf": "^3.0.2",
"shell-path": "2.1.0" "shell-path": "2.1.0"
}, },
"optionalDependencies": { "optionalDependencies": {

View File

@ -1,12 +1,27 @@
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import { cleanUpAppCache, createAppCacheFile } from '../src/app/app-cache-handler'; import * as rimraf from 'rimraf';
import { cleanAppCacheOnInstall, cleanUpAppCache, createAppCacheFile } from '../src/app/app-cache-handler';
import { app, session } from './__mocks__/electron'; import { app, session } from './__mocks__/electron';
jest.mock('fs', () => ({ jest.mock('fs', () => ({
writeFileSync: jest.fn(), writeFileSync: jest.fn(),
existsSync: jest.fn(() => true), existsSync: jest.fn(() => true),
unlinkSync: jest.fn(), unlinkSync: jest.fn(),
readdirSync: jest.fn(() => ['fake1', 'fake2', 'Symphony.config', 'cloudConfig.config']),
lstatSync: jest.fn(() => {
return {
isDirectory: jest.fn(() => true),
};
}),
}));
jest.mock('path', () => ({
join: jest.fn(),
}));
jest.mock('rimraf', () => ({
sync: jest.fn(),
})); }));
jest.mock('../src/common/logger', () => { jest.mock('../src/common/logger', () => {
@ -19,27 +34,53 @@ jest.mock('../src/common/logger', () => {
}); });
describe('app cache handler', () => { describe('app cache handler', () => {
const cachePathExpected = path.join(app.getPath('userData'), 'CacheCheck'); describe('check app cache file', () => {
const cachePathExpected = path.join(app.getPath('userData'), 'CacheCheck');
it('should call `cleanUpAppCache` correctly', () => { it('should call `cleanUpAppCache` correctly', () => {
const spyFn = 'unlinkSync'; const spyFn = 'unlinkSync';
const spy = jest.spyOn(fs, spyFn); const spy = jest.spyOn(fs, spyFn);
cleanUpAppCache(); cleanUpAppCache();
expect(spy).toBeCalledWith(cachePathExpected); expect(spy).toBeCalledWith(cachePathExpected);
});
it('should call `clearCache` when `session.defaultSession` is not null', () => {
jest.spyOn(fs, 'existsSync').mockImplementation(() => false);
const spyFn = 'clearCache';
const spy = jest.spyOn(session.defaultSession, spyFn);
cleanUpAppCache();
expect(spy).toBeCalled();
});
it('should call `createAppCacheFile` correctly', () => {
const spyFn = 'writeFileSync';
const spy = jest.spyOn(fs, spyFn);
createAppCacheFile();
expect(spy).lastCalledWith(cachePathExpected, '');
});
}); });
it('should call `clearCache` when `session.defaultSession` is not null', () => { describe('clean app cache on install', () => {
jest.spyOn(fs, 'existsSync').mockImplementation(() => false); it('should clean app cache and cookies on install', () => {
const spyFn = 'clearCache';
const spy = jest.spyOn(session.defaultSession, spyFn); const pathSpy = jest.spyOn(path, 'join');
cleanUpAppCache();
expect(spy).toBeCalled(); const fsReadDirSpy = jest.spyOn(fs, 'readdirSync');
const fsStatSpy = jest.spyOn(fs, 'lstatSync');
const fsUnlinkSpy = jest.spyOn(fs, 'unlinkSync');
const rimrafSpy = jest.spyOn(rimraf, 'sync');
cleanAppCacheOnInstall();
expect(pathSpy).toBeCalled();
expect(fsReadDirSpy).toBeCalled();
expect(fsStatSpy).toBeCalled();
expect(fsUnlinkSpy).toBeCalled();
expect(rimrafSpy).toBeCalled();
});
}); });
it('should call `createAppCacheFile` correctly', () => {
const spyFn = 'writeFileSync';
const spy = jest.spyOn(fs, spyFn);
createAppCacheFile();
expect(spy).lastCalledWith(cachePathExpected, '');
});
}); });

View File

@ -1,11 +1,37 @@
import { app, session } from 'electron'; import { app, BrowserWindow, dialog, session } from 'electron';
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import * as rimraf from 'rimraf';
import { i18n } from '../common/i18n';
import { logger } from '../common/logger'; import { logger } from '../common/logger';
// Cache check file path // Cache check file path
const cacheCheckFilePath: string = path.join(app.getPath('userData'), 'CacheCheck'); const userDataPath: string = app.getPath('userData');
const cacheCheckFilePath: string = path.join(userDataPath, 'CacheCheck');
/**
* Cleans old cache
*/
const cleanOldCache = (): void => {
const configFilename = 'Symphony.config';
const cloudConfigFilename = 'cloudConfig.config';
const files = fs.readdirSync(userDataPath);
files.forEach((file) => {
const filePath = path.join(userDataPath, file);
if (file === configFilename || file === cloudConfigFilename) {
return;
}
if (fs.lstatSync(filePath).isDirectory()) {
rimraf.sync(filePath);
return;
}
fs.unlinkSync(filePath);
});
};
/** /**
* Deletes app cache file if exists or clears * Deletes app cache file if exists or clears
@ -30,3 +56,45 @@ export const createAppCacheFile = (): void => {
logger.info(`app-cache-handler: this is a clean exit, creating app cache file`); logger.info(`app-cache-handler: this is a clean exit, creating app cache file`);
fs.writeFileSync(cacheCheckFilePath, ''); fs.writeFileSync(cacheCheckFilePath, '');
}; };
/**
* Cleans the app cache on new install
*/
export const cleanAppCacheOnInstall = (): void => {
logger.info(`app-cache-handler: cleaning app cache and cookies on new install`);
cleanOldCache();
};
/**
* Cleans app cache and restarts the app on crash or unresponsive events
* @param window Browser window to listen to for crash events
*/
export const cleanAppCacheOnCrash = (window: BrowserWindow): void => {
logger.info(`app-cache-handler: listening to crash events & cleaning app cache`);
const events = ['unresponsive', 'crashed', 'plugin-crashed'];
events.forEach((windowEvent: any) => {
window.webContents.on(windowEvent, async () => {
logger.info(`app-cache-handler: Window Event '${windowEvent}' occurred. Clearing cache & restarting app`);
const focusedWindow = BrowserWindow.getFocusedWindow();
if (!focusedWindow || (typeof focusedWindow.isDestroyed === 'function' && focusedWindow.isDestroyed())) {
return;
}
const options = {
type: 'question',
title: i18n.t('Relaunch Application')(),
message: i18n.t('Oops! Something went wrong. Would you like to restart the app?')(),
buttons: [i18n.t('Restart')(), i18n.t('Cancel')()],
cancelId: 1,
};
const { response } = await dialog.showMessageBox(focusedWindow, options);
if (response === 0) {
cleanOldCache();
app.relaunch();
app.exit();
}
});
});
};

View File

@ -68,7 +68,7 @@ electron.app.on('certificate-error', async (event, webContents, url, error, _cer
const browserWin = electron.BrowserWindow.fromWebContents(webContents); const browserWin = electron.BrowserWindow.fromWebContents(webContents);
if (browserWin && windowExists(browserWin)) { if (browserWin && windowExists(browserWin)) {
const {response} = await electron.dialog.showMessageBox(browserWin, { const { response } = await electron.dialog.showMessageBox(browserWin, {
type: 'warning', type: 'warning',
buttons: [ buttons: [
i18n.t('Allow')(), i18n.t('Allow')(),
@ -111,7 +111,7 @@ export const showLoadFailure = async (browserWindow: Electron.BrowserWindow, url
if (showDialog) { if (showDialog) {
const { response } = await electron.dialog.showMessageBox(browserWindow, { const { response } = await electron.dialog.showMessageBox(browserWindow, {
type: 'error', type: 'error',
buttons: [ i18n.t('Reload')(), i18n.t('Ignore')() ], buttons: [i18n.t('Reload')(), i18n.t('Ignore')()],
defaultId: 0, defaultId: 0,
cancelId: 1, cancelId: 1,
noLink: true, noLink: true,
@ -157,13 +157,13 @@ export const titleBarChangeDialog = async (isNativeStyle: CloudConfigDataTypes)
title: i18n.t('Relaunch Application')(), title: i18n.t('Relaunch Application')(),
message: i18n.t('Updating Title bar style requires Symphony to relaunch.')(), message: i18n.t('Updating Title bar style requires Symphony to relaunch.')(),
detail: i18n.t('Note: When Hamburger menu is disabled, you can trigger the main menu by pressing the Alt key.')(), detail: i18n.t('Note: When Hamburger menu is disabled, you can trigger the main menu by pressing the Alt key.')(),
buttons: [ i18n.t('Relaunch')(), i18n.t('Cancel')() ], buttons: [i18n.t('Relaunch')(), i18n.t('Cancel')()],
cancelId: 1, cancelId: 1,
}; };
const { response } = await electron.dialog.showMessageBox(focusedWindow, options); const { response } = await electron.dialog.showMessageBox(focusedWindow, options);
if (response === 0) { if (response === 0) {
logger.error(`test`, isNativeStyle); logger.error(`test`, isNativeStyle);
await config.updateUserConfig({ isCustomTitleBar: isNativeStyle }); await config.updateUserConfig({ isCustomTitleBar: isNativeStyle });
app.relaunch(); app.relaunch();
app.exit(); app.exit();
} }
@ -182,7 +182,7 @@ export const gpuRestartDialog = async (disableGpu: boolean) => {
type: 'question', type: 'question',
title: i18n.t('Relaunch Application')(), title: i18n.t('Relaunch Application')(),
message: i18n.t('Would you like to restart and apply these new settings now?')(), message: i18n.t('Would you like to restart and apply these new settings now?')(),
buttons: [ i18n.t('Restart')(), i18n.t('Later')() ], buttons: [i18n.t('Restart')(), i18n.t('Later')()],
cancelId: 1, cancelId: 1,
}; };
const { response } = await electron.dialog.showMessageBox(focusedWindow, options); const { response } = await electron.dialog.showMessageBox(focusedWindow, options);

View File

@ -5,7 +5,7 @@ import * as shellPath from 'shell-path';
import { isDevEnv, isElectronQA, isLinux, isMac } from '../common/env'; import { isDevEnv, isElectronQA, isLinux, isMac } from '../common/env';
import { logger } from '../common/logger'; import { logger } from '../common/logger';
import { getCommandLineArgs } from '../common/utils'; import { getCommandLineArgs } from '../common/utils';
import { cleanUpAppCache, createAppCacheFile } from './app-cache-handler'; import { cleanAppCacheOnInstall, cleanUpAppCache, createAppCacheFile } from './app-cache-handler';
import { autoLaunchInstance } from './auto-launch-controller'; import { autoLaunchInstance } from './auto-launch-controller';
import { setChromeFlags, setSessionProperties } from './chrome-flags'; import { setChromeFlags, setSessionProperties } from './chrome-flags';
import { config } from './config-handler'; import { config } from './config-handler';
@ -81,6 +81,7 @@ const startApplication = async () => {
createAppCacheFile(); createAppCacheFile();
if (config.isFirstTimeLaunch()) { if (config.isFirstTimeLaunch()) {
logger.info(`main: This is a first time launch! will update config and handle auto launch`); logger.info(`main: This is a first time launch! will update config and handle auto launch`);
cleanAppCacheOnInstall();
await config.setUpFirstTimeLaunch(); await config.setUpFirstTimeLaunch();
if (!isLinux) { if (!isLinux) {
await autoLaunchInstance.handleAutoLaunch(); await autoLaunchInstance.handleAutoLaunch();
@ -142,7 +143,7 @@ app.on('window-all-closed', () => {
/** /**
* Creates a new empty cache file when the app is quit * Creates a new empty cache file when the app is quit
*/ */
app.on('quit', () => { app.on('quit', () => {
logger.info(`main: quitting the app!`); logger.info(`main: quitting the app!`);
cleanUpAppCache(); cleanUpAppCache();
}); });

View File

@ -19,6 +19,7 @@ import { i18n, LocaleType } from '../common/i18n';
import { logger } from '../common/logger'; import { logger } from '../common/logger';
import { getCommandLineArgs, getGuid } from '../common/utils'; import { getCommandLineArgs, getGuid } from '../common/utils';
import { notification } from '../renderer/notification'; import { notification } from '../renderer/notification';
import { cleanAppCacheOnCrash } from './app-cache-handler';
import { AppMenu } from './app-menu'; import { AppMenu } from './app-menu';
import { handleChildWindow } from './child-window-handler'; import { handleChildWindow } from './child-window-handler';
import { CloudConfigDataTypes, config, IConfig, IGlobalConfig } from './config-handler'; import { CloudConfigDataTypes, config, IConfig, IGlobalConfig } from './config-handler';
@ -114,14 +115,14 @@ export class WindowHandler {
constructor(opts?: Electron.BrowserViewConstructorOptions) { constructor(opts?: Electron.BrowserViewConstructorOptions) {
// Use these variables only on initial setup // Use these variables only on initial setup
this.config = config.getConfigFields([ 'isCustomTitleBar', 'mainWinPos', 'minimizeOnClose', 'notificationSettings', 'alwaysOnTop', 'locale', 'customFlags', 'clientSwitch', 'enableRendererLogs' ]); this.config = config.getConfigFields(['isCustomTitleBar', 'mainWinPos', 'minimizeOnClose', 'notificationSettings', 'alwaysOnTop', 'locale', 'customFlags', 'clientSwitch', 'enableRendererLogs']);
logger.info(`window-handler: main windows initialized with following config data`, this.config); logger.info(`window-handler: main windows initialized with following config data`, this.config);
this.globalConfig = config.getGlobalConfigFields([ 'url', 'contextIsolation', 'contextOriginUrl' ]); this.globalConfig = config.getGlobalConfigFields(['url', 'contextIsolation', 'contextOriginUrl']);
this.userConfig = config.getUserConfigFields([ 'url' ]); this.userConfig = config.getUserConfigFields(['url']);
const { customFlags } = this.config; const { customFlags } = this.config;
const { disableThrottling } = config.getCloudConfigFields([ 'disableThrottling' ]) as any; const { disableThrottling } = config.getCloudConfigFields(['disableThrottling']) as any;
this.windows = {}; this.windows = {};
this.contextIsolation = this.globalConfig.contextIsolation || false; this.contextIsolation = this.globalConfig.contextIsolation || false;
@ -159,9 +160,9 @@ export class WindowHandler {
i18n.setLocale(locale); i18n.setLocale(locale);
try { try {
const extra = {podUrl: this.userConfig.url ? this.userConfig.url : this.globalConfig.url, process: 'main'}; const extra = { podUrl: this.userConfig.url ? this.userConfig.url : this.globalConfig.url, process: 'main' };
const defaultOpts = {uploadToServer: false, companyName: 'Symphony', submitURL: ''}; const defaultOpts = { uploadToServer: false, companyName: 'Symphony', submitURL: '' };
crashReporter.start({...defaultOpts, extra}); crashReporter.start({ ...defaultOpts, extra });
} catch (e) { } catch (e) {
throw new Error('failed to init crash report'); throw new Error('failed to init crash report');
} }
@ -178,7 +179,7 @@ export class WindowHandler {
logger.info('window-handler: createApplication mainWinPos: ' + JSON.stringify(this.config.mainWinPos)); logger.info('window-handler: createApplication mainWinPos: ' + JSON.stringify(this.config.mainWinPos));
let {isFullScreen, isMaximized} = this.config.mainWinPos ? this.config.mainWinPos : {isFullScreen: false, isMaximized: false}; let { isFullScreen, isMaximized } = this.config.mainWinPos ? this.config.mainWinPos : { isFullScreen: false, isMaximized: false };
this.url = WindowHandler.getValidUrl(this.userConfig.url ? this.userConfig.url : this.globalConfig.url); this.url = WindowHandler.getValidUrl(this.userConfig.url ? this.userConfig.url : this.globalConfig.url);
logger.info(`window-handler: setting url ${this.url} from config file!`); logger.info(`window-handler: setting url ${this.url} from config file!`);
@ -219,7 +220,7 @@ export class WindowHandler {
logger.info('window-handler: windowSize: sizes: ' + JSON.stringify(sizes)); logger.info('window-handler: windowSize: sizes: ' + JSON.stringify(sizes));
DEFAULT_WIDTH = Number(sizes[0]); DEFAULT_WIDTH = Number(sizes[0]);
DEFAULT_HEIGHT = Number(sizes[1]); DEFAULT_HEIGHT = Number(sizes[1]);
if (this.config.mainWinPos ) { if (this.config.mainWinPos) {
this.config.mainWinPos.width = DEFAULT_WIDTH; this.config.mainWinPos.width = DEFAULT_WIDTH;
this.config.mainWinPos.height = DEFAULT_HEIGHT; this.config.mainWinPos.height = DEFAULT_HEIGHT;
} }
@ -245,7 +246,7 @@ export class WindowHandler {
if (urlFromCmd) { if (urlFromCmd) {
const commandLineUrl = urlFromCmd.substr(6); const commandLineUrl = urlFromCmd.substr(6);
logger.info(`window-handler: trying to set url ${commandLineUrl} from command line.`); logger.info(`window-handler: trying to set url ${commandLineUrl} from command line.`);
const { podWhitelist } = config.getConfigFields([ 'podWhitelist' ]); const { podWhitelist } = config.getConfigFields(['podWhitelist']);
logger.info(`window-handler: checking pod whitelist.`); logger.info(`window-handler: checking pod whitelist.`);
if (podWhitelist.length > 0) { if (podWhitelist.length > 0) {
logger.info(`window-handler: pod whitelist is not empty ${podWhitelist}`); logger.info(`window-handler: pod whitelist is not empty ${podWhitelist}`);
@ -289,6 +290,7 @@ export class WindowHandler {
this.handleWelcomeScreen(); this.handleWelcomeScreen();
} }
cleanAppCacheOnCrash(this.mainWindow);
// loads the main window with url from config/cmd line // loads the main window with url from config/cmd line
this.mainWindow.loadURL(this.url); this.mainWindow.loadURL(this.url);
// check for build expiry in case of test builds // check for build expiry in case of test builds
@ -337,7 +339,7 @@ export class WindowHandler {
isMainWindow: true, isMainWindow: true,
}); });
this.appMenu = new AppMenu(); this.appMenu = new AppMenu();
const { permissions } = config.getConfigFields([ 'permissions' ]); const { permissions } = config.getConfigFields(['permissions']);
this.mainWindow.webContents.send('is-screen-share-enabled', permissions.media); this.mainWindow.webContents.send('is-screen-share-enabled', permissions.media);
}); });
@ -357,7 +359,7 @@ export class WindowHandler {
if (href === 'data:text/html,chromewebdata' || href === 'chrome-error://chromewebdata/') { if (href === 'data:text/html,chromewebdata' || href === 'chrome-error://chromewebdata/') {
if (this.mainWindow && windowExists(this.mainWindow)) { 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.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, this.url || this.userConfig.url || this.globalConfig.url); isSymphonyReachable(this.mainWindow, this.url || this.userConfig.url || this.globalConfig.url);
} }
} }
@ -399,7 +401,7 @@ export class WindowHandler {
return this.destroyAllWindows(); return this.destroyAllWindows();
} }
const { minimizeOnClose } = config.getConfigFields([ 'minimizeOnClose' ]); const { minimizeOnClose } = config.getConfigFields(['minimizeOnClose']);
if (minimizeOnClose === CloudConfigDataTypes.ENABLED) { if (minimizeOnClose === CloudConfigDataTypes.ENABLED) {
event.preventDefault(); event.preventDefault();
this.mainWindow.minimize(); this.mainWindow.minimize();
@ -496,7 +498,7 @@ export class WindowHandler {
resource: i18n.loadedResources, resource: i18n.loadedResources,
}); });
const userConfigUrl = this.userConfig.url const userConfigUrl = this.userConfig.url
&& this.userConfig.url.indexOf('/login/sso/initsso') > -1 ? && this.userConfig.url.indexOf('/login/sso/initsso') > -1 ?
this.userConfig.url.slice(0, this.userConfig.url.indexOf('/login/sso/initsso')) this.userConfig.url.slice(0, this.userConfig.url.indexOf('/login/sso/initsso'))
: this.userConfig.url; : this.userConfig.url;
@ -510,7 +512,7 @@ export class WindowHandler {
}); });
ipcMain.on('set-pod-url', async (_event, newPodUrl: string) => { ipcMain.on('set-pod-url', async (_event, newPodUrl: string) => {
await config.updateUserConfig({url: newPodUrl}); await config.updateUserConfig({ url: newPodUrl });
app.relaunch(); app.relaunch();
app.exit(); app.exit();
}); });
@ -777,14 +779,14 @@ export class WindowHandler {
this.screenPickerWindow.webContents.setZoomFactor(1); this.screenPickerWindow.webContents.setZoomFactor(1);
this.screenPickerWindow.webContents.setVisualZoomLevelLimits(1, 1); this.screenPickerWindow.webContents.setVisualZoomLevelLimits(1, 1);
this.screenPickerWindow.webContents.send('screen-picker-data', {sources, id}); this.screenPickerWindow.webContents.send('screen-picker-data', { sources, id });
this.addWindow(opts.winKey, this.screenPickerWindow); this.addWindow(opts.winKey, this.screenPickerWindow);
}); });
ipcMain.once('screen-source-selected', (_event, source) => { ipcMain.once('screen-source-selected', (_event, source) => {
const displays = electron.screen.getAllDisplays(); const displays = electron.screen.getAllDisplays();
logger.info('window-utils: displays.length: ' + displays.length); logger.info('window-utils: displays.length: ' + displays.length);
for (let i = 0, len = displays.length; i < len; i++) { for (let i = 0, len = displays.length; i < len; i++) {
logger.info('window-utils: display[' + i + ']: ' + JSON.stringify(displays[ i ])); logger.info('window-utils: display[' + i + ']: ' + JSON.stringify(displays[i]));
} }
if (source != null) { if (source != null) {
@ -793,22 +795,22 @@ export class WindowHandler {
const type = source.id.split(':')[0]; const type = source.id.split(':')[0];
if (type === 'window') { if (type === 'window') {
const hwnd = source.id.split(':')[1]; const hwnd = source.id.split(':')[1];
this.execCmd(this.screenShareIndicatorFrameUtil, [ hwnd ]); this.execCmd(this.screenShareIndicatorFrameUtil, [hwnd]);
} else if (isMac && type === 'screen') { } else if (isMac && type === 'screen') {
const dispId = source.id.split(':')[1]; const dispId = source.id.split(':')[1];
this.execCmd(this.screenShareIndicatorFrameUtil, [ dispId ]); this.execCmd(this.screenShareIndicatorFrameUtil, [dispId]);
} else if (isWindowsOS && type === 'screen') { } else if (isWindowsOS && type === 'screen') {
logger.info('window-handler: source.display_id: ' + source.display_id); logger.info('window-handler: source.display_id: ' + source.display_id);
if (source.display_id !== '') { if (source.display_id !== '') {
this.execCmd(this.screenShareIndicatorFrameUtil, [ source.display_id ]); this.execCmd(this.screenShareIndicatorFrameUtil, [source.display_id]);
} else { } else {
const dispId = source.id.split(':')[1]; const dispId = source.id.split(':')[1];
const clampedDispId = Math.min(dispId, displays.length - 1); const clampedDispId = Math.min(dispId, displays.length - 1);
const keyId = 'id'; const keyId = 'id';
logger.info('window-utils: dispId: ' + dispId); logger.info('window-utils: dispId: ' + dispId);
logger.info('window-utils: clampedDispId: ' + clampedDispId); logger.info('window-utils: clampedDispId: ' + clampedDispId);
logger.info('window-utils: displays [' + clampedDispId + '] [id]: ' + displays [clampedDispId] [ keyId ]); logger.info('window-utils: displays [' + clampedDispId + '] [id]: ' + displays[clampedDispId][keyId]);
this.execCmd(this.screenShareIndicatorFrameUtil, [ displays [clampedDispId] [ keyId ].toString() ]); this.execCmd(this.screenShareIndicatorFrameUtil, [displays[clampedDispId][keyId].toString()]);
} }
} }
} }
@ -856,7 +858,7 @@ export class WindowHandler {
if (!this.basicAuthWindow || !windowExists(this.basicAuthWindow)) { if (!this.basicAuthWindow || !windowExists(this.basicAuthWindow)) {
return; return;
} }
this.basicAuthWindow.webContents.send('basic-auth-data', {hostname, isValidCredentials: isMultipleTries}); this.basicAuthWindow.webContents.send('basic-auth-data', { hostname, isValidCredentials: isMultipleTries });
}); });
const closeBasicAuth = (_event, shouldClearSettings = true) => { const closeBasicAuth = (_event, shouldClearSettings = true) => {
if (shouldClearSettings) { if (shouldClearSettings) {
@ -869,7 +871,7 @@ export class WindowHandler {
}; };
const login = (_event, arg) => { const login = (_event, arg) => {
const {username, password} = arg; const { username, password } = arg;
callback(username, password); callback(username, password);
closeBasicAuth(null, false); closeBasicAuth(null, false);
}; };
@ -921,17 +923,17 @@ export class WindowHandler {
if (app.isReady()) { if (app.isReady()) {
screens = electron.screen.getAllDisplays(); screens = electron.screen.getAllDisplays();
} }
const { position, display } = config.getConfigFields([ 'notificationSettings' ]).notificationSettings; const { position, display } = config.getConfigFields(['notificationSettings']).notificationSettings;
this.notificationSettingsWindow.webContents.send('notification-settings-data', {screens, position, display}); this.notificationSettingsWindow.webContents.send('notification-settings-data', { screens, position, display });
} }
}); });
this.addWindow(opts.winKey, this.notificationSettingsWindow); this.addWindow(opts.winKey, this.notificationSettingsWindow);
ipcMain.once('notification-settings-update', async (_event, args) => { ipcMain.once('notification-settings-update', async (_event, args) => {
const {display, position} = args; const { display, position } = args;
try { try {
await config.updateUserConfig({notificationSettings: {display, position}}); await config.updateUserConfig({ notificationSettings: { display, position } });
} catch (e) { } catch (e) {
logger.error(`NotificationSettings: Could not update user config file error`, e); logger.error(`NotificationSettings: Could not update user config file error`, e);
} }
@ -991,7 +993,7 @@ export class WindowHandler {
closable: false, closable: false,
}, { }, {
devTools: false, devTools: false,
}), ...{winKey: streamId}, }), ...{ winKey: streamId },
}; };
if (opts.width && opts.height) { if (opts.width && opts.height) {
opts = Object.assign({}, opts, { opts = Object.assign({}, opts, {
@ -1008,7 +1010,7 @@ export class WindowHandler {
logger.info('window-handler: element.id.toString(): ' + element.id.toString()); logger.info('window-handler: element.id.toString(): ' + element.id.toString());
if (displayId === element.id.toString()) { if (displayId === element.id.toString()) {
logger.info(`window-handler: element:`, element); logger.info(`window-handler: element:`, element);
this.createScreenSharingFrameWindow('screen-sharing-frame', this.createScreenSharingFrameWindow('screen-sharing-frame',
element.workArea.width, element.workArea.width,
element.workArea.height, element.workArea.height,
element.workArea.x, element.workArea.x,
@ -1027,7 +1029,7 @@ export class WindowHandler {
if (!this.screenSharingIndicatorWindow || !windowExists(this.screenSharingIndicatorWindow)) { if (!this.screenSharingIndicatorWindow || !windowExists(this.screenSharingIndicatorWindow)) {
return; 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) => { const stopScreenSharing = (_event, indicatorId) => {
if (id === indicatorId) { if (id === indicatorId) {
@ -1229,7 +1231,7 @@ export class WindowHandler {
if (!focusedWindow || !windowExists(focusedWindow)) { if (!focusedWindow || !windowExists(focusedWindow)) {
return; return;
} }
const { devToolsEnabled } = config.getConfigFields([ 'devToolsEnabled' ]); const { devToolsEnabled } = config.getConfigFields(['devToolsEnabled']);
if (devToolsEnabled) { if (devToolsEnabled) {
focusedWindow.webContents.toggleDevTools(); focusedWindow.webContents.toggleDevTools();
return; return;
@ -1333,7 +1335,7 @@ export class WindowHandler {
type: 'error', type: 'error',
title: i18n.t('Build expired')(), 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.')(), 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, cancelId: 0,
}; };
@ -1364,7 +1366,7 @@ export class WindowHandler {
winKey: getGuid(), winKey: getGuid(),
}; };
return {...defaultWindowOpts, ...windowOpts}; return { ...defaultWindowOpts, ...windowOpts };
} }
} }

View File

@ -111,6 +111,7 @@
}, },
"Oops! Looks like we have had a crash.": "Oops! Looks like we have had a crash.", "Oops! Looks like we have had a crash.": "Oops! Looks like we have had a crash.",
"Oops! Looks like we have had a crash. Please reload or close this window.": "Oops! Looks like we have had a crash. Please reload or close this window.", "Oops! Looks like we have had a crash. Please reload or close this window.": "Oops! Looks like we have had a crash. Please reload or close this window.",
"Oops! Something went wrong. Would you like to restart the app?": "Oops! Something went wrong. Would you like to restart the app?",
"Paste": "Paste", "Paste": "Paste",
"Paste and Match Style": "Paste and Match Style", "Paste and Match Style": "Paste and Match Style",
"Permission Denied": "Permission Denied", "Permission Denied": "Permission Denied",

View File

@ -111,6 +111,7 @@
}, },
"Oops! Looks like we have had a crash.": "Oops! Looks like we have had a crash.", "Oops! Looks like we have had a crash.": "Oops! Looks like we have had a crash.",
"Oops! Looks like we have had a crash. Please reload or close this window.": "Oops! Looks like we have had a crash. Please reload or close this window.", "Oops! Looks like we have had a crash. Please reload or close this window.": "Oops! Looks like we have had a crash. Please reload or close this window.",
"Oops! Something went wrong. Would you like to restart the app?": "Oops! Something went wrong. Would you like to restart the app?",
"Paste": "Paste", "Paste": "Paste",
"Paste and Match Style": "Paste and Match Style", "Paste and Match Style": "Paste and Match Style",
"Permission Denied": "Permission Denied", "Permission Denied": "Permission Denied",

View File

@ -112,6 +112,7 @@
}, },
"Oops! Looks like we have had a crash.": "Oops! On dirait que nous avons eu un crash.", "Oops! Looks like we have had a crash.": "Oops! On dirait que nous avons eu un crash.",
"Oops! Looks like we have had a crash. Please reload or close this window.": "Oops! On dirait que nous avons eu un crash. Veuillez recharger ou fermer cette fenêtre.", "Oops! Looks like we have had a crash. Please reload or close this window.": "Oops! On dirait que nous avons eu un crash. Veuillez recharger ou fermer cette fenêtre.",
"Oops! Something went wrong. Would you like to restart the app?": "Oups ! Quelque chose s'est mal passé. Voulez-vous redémarrer l'application ?",
"Paste": "Coller", "Paste": "Coller",
"Paste and Match Style": "Coller et appliquer le style", "Paste and Match Style": "Coller et appliquer le style",
"Permission Denied": "Permission refusée", "Permission Denied": "Permission refusée",

View File

@ -111,6 +111,7 @@
}, },
"Oops! Looks like we have had a crash.": "Oops! On dirait que nous avons eu un crash.", "Oops! Looks like we have had a crash.": "Oops! On dirait que nous avons eu un crash.",
"Oops! Looks like we have had a crash. Please reload or close this window.": "Oops! On dirait que nous avons eu un crash. Veuillez recharger ou fermer cette fenêtre.", "Oops! Looks like we have had a crash. Please reload or close this window.": "Oops! On dirait que nous avons eu un crash. Veuillez recharger ou fermer cette fenêtre.",
"Oops! Something went wrong. Would you like to restart the app?": "Oups ! Quelque chose s'est mal passé. Voulez-vous redémarrer l'application ?",
"Paste": "Coller", "Paste": "Coller",
"Paste and Match Style": "Coller et appliquer le style", "Paste and Match Style": "Coller et appliquer le style",
"Permission Denied": "Permission refusée", "Permission Denied": "Permission refusée",

View File

@ -111,6 +111,7 @@
}, },
"Oops! Looks like we have had a crash.": "おっと!クラッシュしたようです。", "Oops! Looks like we have had a crash.": "おっと!クラッシュしたようです。",
"Oops! Looks like we have had a crash. Please reload or close this window.": "おっと!クラッシュしたようです。このウィンドウを再度読み込むか閉じてください。", "Oops! Looks like we have had a crash. Please reload or close this window.": "おっと!クラッシュしたようです。このウィンドウを再度読み込むか閉じてください。",
"Oops! Something went wrong. Would you like to restart the app?": "おっと!何か問題が起こった。アプリを再開しますか?",
"Paste": "貼り付け", "Paste": "貼り付け",
"Paste and Match Style": "貼り付けでスタイルを合わせる", "Paste and Match Style": "貼り付けでスタイルを合わせる",
"Permission Denied": "アクセス許可が拒否されています", "Permission Denied": "アクセス許可が拒否されています",

View File

@ -111,6 +111,7 @@
}, },
"Oops! Looks like we have had a crash.": "おっと!クラッシュしたようです。", "Oops! Looks like we have had a crash.": "おっと!クラッシュしたようです。",
"Oops! Looks like we have had a crash. Please reload or close this window.": "おっと!クラッシュしたようです。このウィンドウを再度読み込むか閉じてください。", "Oops! Looks like we have had a crash. Please reload or close this window.": "おっと!クラッシュしたようです。このウィンドウを再度読み込むか閉じてください。",
"Oops! Something went wrong. Would you like to restart the app?": "おっと!何か問題が起こった。アプリを再開しますか?",
"Paste": "貼り付け", "Paste": "貼り付け",
"Paste and Match Style": "貼り付けでスタイルを合わせる", "Paste and Match Style": "貼り付けでスタイルを合わせる",
"Permission Denied": "アクセス許可が拒否されています", "Permission Denied": "アクセス許可が拒否されています",