Typescript - Optimize window validation and fix screen picker issue

This commit is contained in:
Kiran Niranjan 2019-01-18 16:32:02 +05:30
parent 9c138ddf25
commit 152839972e
7 changed files with 106 additions and 84 deletions

View File

@ -189,7 +189,7 @@ export class WindowHandler {
// Event needed to hide native menu bar on Windows 10 as we use custom menu bar
this.mainWindow.webContents.once('did-start-loading', () => {
if ((this.config.isCustomTitleBar || isWindowsOS) && this.mainWindow && !this.mainWindow.isDestroyed()) {
if ((this.config.isCustomTitleBar || isWindowsOS) && this.mainWindow && this.windowExists(this.mainWindow)) {
this.mainWindow.setMenuBarVisibility(false);
}
});
@ -211,7 +211,7 @@ export class WindowHandler {
if (!this.isOnline && this.mainWindow) showNetworkConnectivityError(this.mainWindow, this.url, retry);
// early exit if the window has already been destroyed
if (!this.mainWindow || this.mainWindow.isDestroyed()) return;
if (!this.mainWindow || !this.windowExists(this.mainWindow)) return;
this.url = this.mainWindow.webContents.getURL();
// Injects custom title bar css into the webContents
@ -239,7 +239,7 @@ export class WindowHandler {
// Handle main window close
this.mainWindow.on('close', (event) => {
if (!this.mainWindow || this.mainWindow.isDestroyed()) return;
if (!this.mainWindow || !this.windowExists(this.mainWindow)) return;
if (this.willQuitApp) return this.destroyAllWindow();
@ -290,7 +290,14 @@ export class WindowHandler {
public closeWindow(windowType: WindowTypes): void {
switch (windowType) {
case 'screen-picker':
if (this.screenPickerWindow && !this.screenPickerWindow.isDestroyed()) this.screenPickerWindow.close();
if (this.screenPickerWindow && this.windowExists(this.screenPickerWindow)) this.screenPickerWindow.close();
break;
case 'screen-sharing-indicator':
if (this.screenSharingIndicatorWindow
&& this.windowExists(this.screenSharingIndicatorWindow)) this.screenSharingIndicatorWindow.close();
break;
default:
break;
}
}
@ -322,7 +329,7 @@ export class WindowHandler {
public showLoadingScreen(): void {
this.loadingWindow = createComponentWindow('loading-screen', WindowHandler.getLoadingWindowOpts());
this.loadingWindow.webContents.once('did-finish-load', () => {
if (!this.loadingWindow || this.loadingWindow.isDestroyed()) return;
if (!this.loadingWindow || !this.windowExists(this.loadingWindow)) return;
this.loadingWindow.webContents.send('data');
});
@ -335,7 +342,7 @@ export class WindowHandler {
public createAboutAppWindow(): void {
this.aboutAppWindow = createComponentWindow('about-app');
this.aboutAppWindow.webContents.once('did-finish-load', () => {
if (!this.aboutAppWindow || this.aboutAppWindow.isDestroyed()) return;
if (!this.aboutAppWindow || !this.windowExists(this.aboutAppWindow)) return;
this.aboutAppWindow.webContents.send('about-app-data', { buildNumber, clientVersion, version });
});
}
@ -346,7 +353,7 @@ export class WindowHandler {
public createMoreInfoWindow(): void {
this.moreInfoWindow = createComponentWindow('more-info');
this.moreInfoWindow.webContents.once('did-finish-load', () => {
if (!this.moreInfoWindow || this.moreInfoWindow.isDestroyed()) return;
if (!this.moreInfoWindow || !this.windowExists(this.moreInfoWindow)) return;
this.moreInfoWindow.webContents.send('more-info-data');
});
}
@ -354,25 +361,30 @@ export class WindowHandler {
/**
* Creates a screen picker window
*
* @param win
* @param window
* @param sources
* @param id
*/
public createScreenPickerWindow(win: Electron.WebContents, sources: DesktopCapturerSource[], id: number): void {
public createScreenPickerWindow(window: Electron.WebContents, sources: DesktopCapturerSource[], id: number): void {
if (this.screenPickerWindow && this.windowExists(this.screenPickerWindow)) this.screenPickerWindow.close();
const opts = WindowHandler.getScreenPickerWindowOpts();
this.screenPickerWindow = createComponentWindow('screen-picker', opts);
this.screenPickerWindow.webContents.once('did-finish-load', () => {
if (!this.screenPickerWindow || this.screenPickerWindow.isDestroyed()) return;
if (!this.screenPickerWindow || !this.windowExists(this.screenPickerWindow)) return;
this.screenPickerWindow.webContents.send('screen-picker-data', { sources, id });
this.addWindow(opts.winKey, this.screenPickerWindow);
this.screenPickerWindow.once('closed', () => {
this.removeWindow(opts.winKey);
this.screenPickerWindow = null;
});
ipcMain.once('screen-source-selected', (_event, source) => {
win.send('start-share' + id, source);
});
});
ipcMain.once('screen-source-selected', (_event, source) => {
window.send('start-share' + id, source);
if (this.screenPickerWindow && this.windowExists(this.screenPickerWindow)) {
this.screenPickerWindow.close();
}
});
this.screenPickerWindow.once('closed', () => {
this.removeWindow(opts.winKey);
this.screenPickerWindow = null;
});
}
@ -386,12 +398,13 @@ export class WindowHandler {
*/
public createScreenSharingIndicatorWindow(screenSharingWebContents: Electron.webContents, displayId: string, id: number): void {
if (this.screenSharingIndicatorWindow && !this.screenSharingIndicatorWindow.isDestroyed()) {
this.screenSharingIndicatorWindow.close();
this.screenSharingIndicatorWindow = null;
}
if (this.screenSharingIndicatorWindow
&& this.windowExists(this.screenSharingIndicatorWindow)) this.screenSharingIndicatorWindow.close();
const indicatorScreen =
(displayId && electron.screen.getAllDisplays().filter((d) =>
displayId.includes(d.id.toString()))[ 0 ]) || electron.screen.getPrimaryDisplay();
const indicatorScreen = (displayId && electron.screen.getAllDisplays().filter((d) => displayId.includes(d.id.toString()))[ 0 ]) || electron.screen.getPrimaryDisplay();
const screenRect = indicatorScreen.workArea;
let opts = WindowHandler.getScreenSharingIndicatorOpts();
if (opts.width && opts.height) {
@ -403,29 +416,20 @@ export class WindowHandler {
this.screenSharingIndicatorWindow = createComponentWindow('screen-sharing-indicator', opts);
this.screenSharingIndicatorWindow.setVisibleOnAllWorkspaces(true);
this.screenSharingIndicatorWindow.webContents.once('did-finish-load', () => {
if (!this.screenSharingIndicatorWindow || this.screenSharingIndicatorWindow.isDestroyed()) return;
if (!this.screenSharingIndicatorWindow || !this.windowExists(this.screenSharingIndicatorWindow)) return;
this.screenSharingIndicatorWindow.webContents.send('screen-sharing-indicator-data', { id });
const stopScreenSharing = (_event, indicatorId) => {
if (id === indicatorId) {
screenSharingWebContents.send('screen-sharing-stopped', id);
}
};
const destroyScreenSharingIndicator = (_event, indicatorId) => {
if (id === indicatorId && this.screenSharingIndicatorWindow && !this.screenSharingIndicatorWindow.isDestroyed()) {
this.screenSharingIndicatorWindow.close();
}
};
this.screenSharingIndicatorWindow.once('close', () => {
ipcMain.removeListener('stop-screen-sharing', stopScreenSharing);
ipcMain.removeListener('destroy-screen-sharing-indicator', destroyScreenSharingIndicator);
});
ipcMain.once('stop-screen-sharing', stopScreenSharing);
ipcMain.once('destroy-screen-sharing-indicator', destroyScreenSharingIndicator);
});
const stopScreenSharing = (_event, indicatorId) => {
if (id === indicatorId) {
screenSharingWebContents.send('screen-sharing-stopped', id);
}
};
this.screenSharingIndicatorWindow.once('close', () => {
ipcMain.removeListener('stop-screen-sharing', stopScreenSharing);
});
ipcMain.once('stop-screen-sharing', stopScreenSharing);
}
/**
@ -446,31 +450,30 @@ export class WindowHandler {
this.basicAuthWindow = createComponentWindow('basic-auth', opts);
this.basicAuthWindow.setVisibleOnAllWorkspaces(true);
this.basicAuthWindow.webContents.once('did-finish-load', () => {
if (!this.basicAuthWindow || this.basicAuthWindow.isDestroyed()) return;
if (!this.basicAuthWindow || !this.windowExists(this.basicAuthWindow)) return;
this.basicAuthWindow.webContents.send('basic-auth-data', { hostname, isValidCredentials: isMultipleTries });
const closeBasicAuth = (shouldClearSettings = true) => {
if (shouldClearSettings) clearSettings();
if (this.basicAuthWindow && !this.basicAuthWindow.isDestroyed()) {
this.basicAuthWindow.close();
this.basicAuthWindow = null;
}
};
const login = (_event, arg) => {
const { username, password } = arg;
callback(username, password);
closeBasicAuth(false);
};
this.basicAuthWindow.on('close', () => {
ipcMain.removeListener('basic-auth-closed', closeBasicAuth);
ipcMain.removeListener('basic-auth-login', login);
});
ipcMain.once('basic-auth-closed', closeBasicAuth);
ipcMain.once('basic-auth-login', login);
});
const closeBasicAuth = (shouldClearSettings = true) => {
if (shouldClearSettings) clearSettings();
if (this.basicAuthWindow && !this.windowExists(this.basicAuthWindow)) {
this.basicAuthWindow.close();
this.basicAuthWindow = null;
}
};
const login = (_event, arg) => {
const { username, password } = arg;
callback(username, password);
closeBasicAuth(false);
};
this.basicAuthWindow.on('close', () => {
ipcMain.removeListener('basic-auth-closed', closeBasicAuth);
ipcMain.removeListener('basic-auth-login', login);
});
ipcMain.once('basic-auth-closed', closeBasicAuth);
ipcMain.once('basic-auth-login', login);
}
/**
@ -509,13 +512,21 @@ export class WindowHandler {
private destroyAllWindow(): 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);
}
}
this.mainWindow = null;
}
/**
* Checks if window is valid and exists
*
* @param window
* @return boolean
*/
private windowExists = (window: BrowserWindow): boolean => !!window && typeof window.isDestroyed === 'function' && !window.isDestroyed();
/**
* Main window opts
*/

View File

@ -48,7 +48,7 @@ export interface IApiArgs {
type: string;
}
export type WindowTypes = 'screen-picker';
export type WindowTypes = 'screen-picker' | 'screen-sharing-indicator';
/**
* Activity detection

View File

View File

@ -30,13 +30,15 @@ const enum keyCode {
escapeKey = 27,
}
type inputChangeEvent = React.ChangeEvent<HTMLInputElement>;
export default class ScreenPicker extends React.Component<{}, IState> {
private isScreensAvailable: boolean;
private isApplicationsAvailable: boolean;
private readonly eventHandlers = {
onSelect: (src) => this.select(src),
onToggle: (tab) => this.toggle(tab),
onSelect: (src: Electron.DesktopCapturerSource) => this.select(src),
onToggle: (tab: tabs) => (_event: inputChangeEvent) => this.toggle(tab),
onClose: () => this.close(),
onSubmit: () => this.submit(),
};
@ -183,7 +185,7 @@ export default class ScreenPicker extends React.Component<{}, IState> {
type='radio'
name='tabs'
checked={selectedTab === 'screens'}
onChange={() => this.eventHandlers.onToggle('screens')}
onChange={this.eventHandlers.onToggle('screens')}
/>,
<label className={classNames('screens', { hidden: !this.isScreensAvailable })}
htmlFor='screen-tab'
@ -196,7 +198,7 @@ export default class ScreenPicker extends React.Component<{}, IState> {
type='radio'
name='tabs'
checked={selectedTab === 'applications'}
onChange={() => this.eventHandlers.onToggle('applications')}
onChange={this.eventHandlers.onToggle('applications')}
/>,
<label className={classNames('applications', { hidden: !this.isApplicationsAvailable })}
htmlFor='application-tab'
@ -213,7 +215,7 @@ export default class ScreenPicker extends React.Component<{}, IState> {
type='radio'
name='tabs'
checked={true}
onChange={() => this.eventHandlers.onToggle('screens')}
onChange={this.eventHandlers.onToggle('screens')}
/>,
<label className={classNames('screens', { hidden: !this.isScreensAvailable })}
htmlFor='screen-tab'
@ -230,7 +232,7 @@ export default class ScreenPicker extends React.Component<{}, IState> {
type='radio'
name='tabs'
checked={true}
onChange={() => this.eventHandlers.onToggle('applications')}
onChange={this.eventHandlers.onToggle('applications')}
/>,
<label className={classNames('applications', { hidden: !this.isApplicationsAvailable })}
htmlFor='application-tab'
@ -274,6 +276,8 @@ export default class ScreenPicker extends React.Component<{}, IState> {
* Closes the screen picker window
*/
private close(): void {
// setting null will clean up listeners
ipcRenderer.send('screen-source-selected', null);
ipcRenderer.send(apiName.symphonyApi, {
cmd: apiCmds.closeWindow,
windowType: 'screen-picker',
@ -288,7 +292,6 @@ export default class ScreenPicker extends React.Component<{}, IState> {
const { selectedSource } = this.state;
if (selectedSource) {
ipcRenderer.send('screen-source-selected', selectedSource);
this.eventHandlers.onClose();
}
}

View File

@ -2,6 +2,7 @@ import classNames from 'classnames';
import { ipcRenderer, remote } from 'electron';
import * as React from 'react';
import { apiCmds, apiName } from '../../common/api-interface';
import { isMac } from '../../common/env';
import { i18n } from '../../common/i18n-preload';
@ -9,14 +10,15 @@ interface IState {
id: number;
}
type mouseEventButton = React.MouseEvent<HTMLButtonElement>;
/**
* Window that display a banner when the users starting sharing screen
*/
export default class ScreenSharingIndicator extends React.Component<{}, IState> {
private readonly eventHandlers = {
onStopScreenSharing: (id) => this.stopScreenShare(id),
onClose: (id) => this.close(id),
onStopScreenSharing: (id: number) => (_event: mouseEventButton) => this.stopScreenShare(id),
onClose: () => this.close(),
};
constructor(props) {
@ -38,8 +40,8 @@ export default class ScreenSharingIndicator extends React.Component<{}, IState>
<span className='drag-area'/>
<span className='text-label'>{i18n.t(`You are sharing your screen on {appName}`, namespace)({ appName: remote.app.getName() })}</span>
<span className='buttons'>
<a className='hide-button' href='#' onClick={() => this.eventHandlers.onClose(id)}>{i18n.t('Hide', namespace)()}</a>
<button className='stop-sharing-button' onClick={() => this.eventHandlers.onStopScreenSharing(id)}>
<a className='hide-button' href='#' onClick={this.eventHandlers.onClose}>{i18n.t('Hide', namespace)()}</a>
<button className='stop-sharing-button' onClick={this.eventHandlers.onStopScreenSharing(id)}>
{i18n.t('Stop sharing', namespace)()}
</button>
</span>
@ -66,11 +68,12 @@ export default class ScreenSharingIndicator extends React.Component<{}, IState>
/**
* Closes the screen sharing indicator window
*
* @param id
*/
private close(id): void {
ipcRenderer.send('destroy-screen-sharing-indicator', id);
private close(): void {
ipcRenderer.send(apiName.symphonyApi, {
cmd: apiCmds.closeWindow,
windowType: 'screen-sharing-indicator',
});
}
/**

View File

@ -257,6 +257,11 @@ local.ipcRenderer.on('boundsChange', (_event, arg: IBoundsChange): void => {
local.ipcRenderer.on('screen-sharing-stopped', () => {
if (typeof local.screenSharingIndicatorCallback === 'function') {
local.screenSharingIndicatorCallback({ type: 'stopRequested' });
// closes the screen sharing indicator
ipcRenderer.send(apiName.symphonyApi, {
cmd: apiCmds.closeWindow,
windowType: 'screen-sharing-indicator',
});
}
});