mirror of
https://github.com/finos/SymphonyElectron.git
synced 2024-12-28 09:51:06 -06:00
fix: ELECTRON-1022 (Fix issues with notification and origin) (#673)
* ELECTRON-1022 - Fix issues with notification and origin * Typescript - Add Notification APIs to window.ssf object * Typescript - Fix unit test * Typescript - Fix notification bg color issue
This commit is contained in:
parent
10d3d49c44
commit
1e953b0092
@ -215,7 +215,27 @@
|
||||
method: 'notification',
|
||||
};
|
||||
|
||||
window.postMessage({ method: 'notification', data: notf }, '*');
|
||||
if (window.ssf) {
|
||||
const ssfNotificationHandler = new window.ssf.Notification(notf.title, notf);
|
||||
const onclick = (event) => {
|
||||
event.target.close();
|
||||
alert('notification clicked: ' + event.target.data.title);
|
||||
};
|
||||
ssfNotificationHandler.addEventListener('click', onclick);
|
||||
|
||||
const onclose = () => {
|
||||
alert('notification closed');
|
||||
};
|
||||
ssfNotificationHandler.addEventListener('close', onclose);
|
||||
|
||||
const onerror = (event) => {
|
||||
alert('error=' + event.result);
|
||||
};
|
||||
ssfNotificationHandler.addEventListener('error', onerror);
|
||||
} else {
|
||||
window.postMessage({ method: 'notification', data: notf }, '*');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -62,6 +62,7 @@ jest.mock('../src/common/logger', () => {
|
||||
return {
|
||||
logger: {
|
||||
setLoggerWindow: jest.fn(),
|
||||
error: jest.fn(),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
@ -126,7 +126,8 @@ export const handleChildWindow = (webContents: WebContents): void => {
|
||||
const childWebContents: WebContents = newWinOptions.webContents;
|
||||
// Event needed to hide native menu bar
|
||||
childWebContents.once('did-start-loading', () => {
|
||||
const browserWin = BrowserWindow.fromWebContents(childWebContents);
|
||||
const browserWin = BrowserWindow.fromWebContents(childWebContents) as ICustomBrowserWindow;
|
||||
browserWin.origin = config.getGlobalConfigFields([ 'url' ]).url;
|
||||
if (isWindowsOS && browserWin && !browserWin.isDestroyed()) {
|
||||
browserWin.setMenuBarVisibility(false);
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import {
|
||||
*/
|
||||
ipcMain.on(apiName.symphonyApi, (event: Electron.Event, arg: IApiArgs) => {
|
||||
if (!isValidWindow(BrowserWindow.fromWebContents(event.sender))) {
|
||||
logger.error(`main-api-handler: invalid window try to perform action, ignoring action`, arg.cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@ interface ICustomBrowserWindowConstructorOpts extends Electron.BrowserWindowCons
|
||||
export interface ICustomBrowserWindow extends Electron.BrowserWindow {
|
||||
winName: string;
|
||||
notificationObj?: object;
|
||||
origin?: string;
|
||||
}
|
||||
|
||||
// Default window width & height
|
||||
@ -234,6 +235,8 @@ export class WindowHandler {
|
||||
this.mainWindow.loadURL(this.url);
|
||||
// check for build expiry in case of test builds
|
||||
this.checkExpiry(this.mainWindow);
|
||||
// need this for postMessage origin
|
||||
this.mainWindow.origin = this.globalConfig.url;
|
||||
this.mainWindow.webContents.on('did-finish-load', async () => {
|
||||
logger.info(`window-handler: main window web contents finished loading!`);
|
||||
// early exit if the window has already been destroyed
|
||||
|
@ -223,7 +223,7 @@ export const isValidWindow = (browserWin: Electron.BrowserWindow): boolean => {
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
logger.warn('window-utils: invalid window try to perform action, ignoring action');
|
||||
logger.warn(`window-utils: invalid window try to perform action, ignoring action`);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -112,6 +112,11 @@ export interface INotificationData {
|
||||
displayTime: number;
|
||||
}
|
||||
|
||||
export enum NotificationActions {
|
||||
notificationClicked = 'notification-clicked',
|
||||
notificationClosed = 'notification-closed',
|
||||
}
|
||||
|
||||
/**
|
||||
* Screen sharing Indicator
|
||||
*/
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { ipcRenderer, remote } from 'electron';
|
||||
import { remote } from 'electron';
|
||||
|
||||
import {
|
||||
apiCmds,
|
||||
apiName,
|
||||
IBoundsChange,
|
||||
ILogMsg, INotificationData,
|
||||
IScreenSharingIndicator, IScreenSharingIndicatorOptions,
|
||||
@ -60,7 +59,9 @@ export class AppBridge {
|
||||
constructor() {
|
||||
// starts with corporate pod and
|
||||
// will be updated with the global config url
|
||||
this.origin = ipcRenderer.sendSync(apiName.symphonyApi, { cmd: apiCmds.getConfigUrl });
|
||||
const currentWindow = remote.getCurrentWindow();
|
||||
// @ts-ignore
|
||||
this.origin = currentWindow.origin || '';
|
||||
if (ssInstance && typeof ssInstance.setBroadcastMessage === 'function') {
|
||||
ssInstance.setBroadcastMessage(this.broadcastMessage);
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ interface IState {
|
||||
company: string;
|
||||
body: string;
|
||||
image: string;
|
||||
icon: string;
|
||||
id: number;
|
||||
color: string;
|
||||
}
|
||||
@ -31,6 +32,7 @@ export default class NotificationComp extends React.Component<{}, IState> {
|
||||
company: 'Symphony',
|
||||
body: '',
|
||||
image: '',
|
||||
icon: '',
|
||||
id: 0,
|
||||
color: '',
|
||||
};
|
||||
@ -49,11 +51,12 @@ export default class NotificationComp extends React.Component<{}, IState> {
|
||||
* Renders the custom title bar
|
||||
*/
|
||||
public render(): JSX.Element {
|
||||
const { title, company, body, image, id, color } = this.state;
|
||||
const isLightTheme = color ? color.match(whiteColorRegExp) : true;
|
||||
const { title, company, body, image, icon, id, color } = this.state;
|
||||
const colorHex = color ? '#' + color : undefined;
|
||||
const isLightTheme = colorHex ? colorHex.match(whiteColorRegExp) : true;
|
||||
|
||||
const theme = classNames({ light: isLightTheme, dark: !isLightTheme });
|
||||
const bgColor = { backgroundColor: color || '#ffffff' };
|
||||
const bgColor = { backgroundColor: colorHex || '#ffffff' };
|
||||
|
||||
return (
|
||||
<div className='container' style={bgColor} onClick={this.eventHandlers.onClick(id)}>
|
||||
@ -62,11 +65,11 @@ export default class NotificationComp extends React.Component<{}, IState> {
|
||||
</div>
|
||||
<div className='header'>
|
||||
<span className={`title ${theme}`}>{title}</span>
|
||||
<span className='company' style={{color: color || '#4a4a4a'}}>{company}</span>
|
||||
<span className='company' style={{color: colorHex || '#4a4a4a'}}>{company}</span>
|
||||
<span className={`message ${theme}`}>{body}</span>
|
||||
</div>
|
||||
<div className='user-profile-pic-container'>
|
||||
<img src={image} className='user-profile-pic' alt='user profile picture'/>
|
||||
<img src={image || icon} className='user-profile-pic' alt='user profile picture'/>
|
||||
</div>
|
||||
<div className='close' title={i18n.t('Close')()} onClick={this.eventHandlers.onClose(id)}>
|
||||
<svg fill='#000000' height='16' viewBox='0 0 24 24' width='16' xmlns='http://www.w3.org/2000/svg'>
|
||||
|
89
src/renderer/notification-ssf-hendler.ts
Normal file
89
src/renderer/notification-ssf-hendler.ts
Normal file
@ -0,0 +1,89 @@
|
||||
import { remote } from 'electron';
|
||||
|
||||
import { INotificationData, NotificationActions } from '../common/api-interface';
|
||||
const notification = remote.require('../renderer/notification').notification;
|
||||
|
||||
let latestID = 0;
|
||||
|
||||
/**
|
||||
* Implementation for notifications interface,
|
||||
* this class is to mock the Window.Notification interface
|
||||
*/
|
||||
export default class SSFNotificationHandler {
|
||||
|
||||
public _data: INotificationData;
|
||||
|
||||
private readonly id: number;
|
||||
private readonly eventHandlers = {
|
||||
onClick: (event: NotificationActions, _data: INotificationData) => this.notificationClicked(event),
|
||||
};
|
||||
private notificationClickCallback: (({ target }) => {}) | undefined;
|
||||
private notificationCloseCallback: (({ target }) => {}) | undefined;
|
||||
|
||||
constructor(title, options) {
|
||||
this.id = latestID;
|
||||
latestID++;
|
||||
this._data = { ...title, ...options };
|
||||
notification.showNotification(this._data, this.eventHandlers.onClick);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes notification
|
||||
*/
|
||||
public close(): void {
|
||||
notification.hideNotification(this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Always allow showing notifications.
|
||||
* @return {string} 'granted'
|
||||
*/
|
||||
static get permission(): string {
|
||||
return 'granted';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns data object passed in via constructor options
|
||||
*/
|
||||
get data(): INotificationData {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds event listeners for 'click', 'close', 'show', 'error' events
|
||||
*
|
||||
* @param {String} event event to listen for
|
||||
* @param {func} cb callback invoked when event occurs
|
||||
*/
|
||||
public addEventListener(event: string, cb: () => {}): void {
|
||||
if (event && typeof cb === 'function') {
|
||||
switch (event) {
|
||||
case 'click':
|
||||
this.notificationClickCallback = cb;
|
||||
break;
|
||||
case 'close':
|
||||
this.notificationCloseCallback = cb;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the callback based on the event name
|
||||
*
|
||||
* @param event {NotificationActions}
|
||||
*/
|
||||
private notificationClicked(event: NotificationActions): void {
|
||||
switch (event) {
|
||||
case NotificationActions.notificationClicked:
|
||||
if (this.notificationClickCallback && typeof this.notificationClickCallback === 'function') {
|
||||
this.notificationClickCallback({ target: this });
|
||||
}
|
||||
break;
|
||||
case NotificationActions.notificationClosed:
|
||||
if (this.notificationCloseCallback && typeof this.notificationCloseCallback === 'function') {
|
||||
this.notificationCloseCallback({ target: this });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ import { app, ipcMain } from 'electron';
|
||||
import { config } from '../app/config-handler';
|
||||
import { createComponentWindow, windowExists } from '../app/window-utils';
|
||||
import { AnimationQueue } from '../common/animation-queue';
|
||||
import { apiName, INotificationData } from '../common/api-interface';
|
||||
import { apiName, INotificationData, NotificationActions } from '../common/api-interface';
|
||||
import { logger } from '../common/logger';
|
||||
import NotificationHandler from './notification-handler';
|
||||
|
||||
@ -60,6 +60,8 @@ class Notification extends NotificationHandler {
|
||||
constructor(opts) {
|
||||
super(opts);
|
||||
ipcMain.on('close-notification', (_event, windowId) => {
|
||||
// removes the event listeners on the client side
|
||||
this.notificationClosed(windowId);
|
||||
this.hideNotification(windowId);
|
||||
});
|
||||
|
||||
@ -160,6 +162,7 @@ class Notification extends NotificationHandler {
|
||||
*/
|
||||
public setNotificationContent(notificationWindow: ICustomBrowserWindow, data: INotificationData): void {
|
||||
notificationWindow.clientId = data.id;
|
||||
notificationWindow.notificationData = data;
|
||||
const displayTime = data.displayTime ? data.displayTime : notificationSettings.displayTime;
|
||||
let timeoutId;
|
||||
|
||||
@ -218,12 +221,29 @@ class Notification extends NotificationHandler {
|
||||
const data = browserWindow.notificationData;
|
||||
const callback = this.notificationCallbacks[ clientId ];
|
||||
if (typeof callback === 'function') {
|
||||
this.notificationCallbacks[ clientId ]('notification-clicked', data);
|
||||
callback(NotificationActions.notificationClicked, data);
|
||||
}
|
||||
this.hideNotification(clientId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles notification close which updates client
|
||||
* to remove event listeners
|
||||
*
|
||||
* @param clientId {number}
|
||||
*/
|
||||
public notificationClosed(clientId): void {
|
||||
const browserWindow = this.getNotificationWindow(clientId);
|
||||
if (browserWindow && windowExists(browserWindow) && browserWindow.notificationData) {
|
||||
const data = browserWindow.notificationData;
|
||||
const callback = this.notificationCallbacks[ clientId ];
|
||||
if (typeof callback === 'function') {
|
||||
callback(NotificationActions.notificationClosed, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the notification based on the client id
|
||||
*
|
||||
|
@ -18,6 +18,7 @@ import {
|
||||
import { i18n, LocaleType } from '../common/i18n-preload';
|
||||
import { throttle } from '../common/utils';
|
||||
import { getSource } from './desktop-capturer';
|
||||
import SSFNotificationHandler from './notification-ssf-hendler';
|
||||
import { ScreenSnippetBcHandler } from './screen-snippet-bc-handler';
|
||||
|
||||
let isAltKey: boolean = false;
|
||||
@ -127,6 +128,8 @@ export class SSFApi {
|
||||
|
||||
public SearchUtils: any = swiftSearchUtils; // tslint:disable-line
|
||||
|
||||
public Notification = SSFNotificationHandler; // tslint:disable-line
|
||||
|
||||
/**
|
||||
* Implements equivalent of desktopCapturer.getSources - that works in
|
||||
* a sandboxed renderer process.
|
||||
|
Loading…
Reference in New Issue
Block a user