Skip to content

Commit

Permalink
SDA-4070 Presence status on macOS (finos#1738)
Browse files Browse the repository at this point in the history
* SDA-4070 Presence status on macOS

* bugfix

* SDA-4070 Presence status on macOS

* SDA-4070 Presence status on macOS
  • Loading branch information
sbenmoussati committed Mar 27, 2023
1 parent f82ef2f commit f780a05
Show file tree
Hide file tree
Showing 48 changed files with 158 additions and 29 deletions.
2 changes: 2 additions & 0 deletions src/app/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { protocolHandler } from './protocol-handler';
import { ICustomBrowserWindow, windowHandler } from './window-handler';

import { autoLaunchInstance } from './auto-launch-controller';
import { presenceStatusStore } from './stores';

// Set automatic period substitution to false because of a bug in draft js on the client app
// See https://perzoinc.atlassian.net/browse/SDA-2215 for more details
Expand Down Expand Up @@ -171,6 +172,7 @@ app.on('window-all-closed', () => {
*/
app.on('quit', () => {
logger.info(`main: quitting the app!`);
presenceStatusStore.destroyCurrentTray();
cleanUpAppCache();
});

Expand Down
99 changes: 94 additions & 5 deletions src/app/presence-status-handler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { nativeImage, WebContents } from 'electron';
import { app, Menu, nativeImage, WebContents } from 'electron';
import {
EPresenceStatus,
IPresenceStatus,
Expand All @@ -7,7 +7,8 @@ import {
import { i18n } from '../common/i18n';
import { logger } from '../common/logger';
import { presenceStatusStore } from './stores';
import { showBadgeCount, showSystemTrayPresence } from './window-utils';
import { windowHandler } from './window-handler';
import { initSysTray, showBadgeCount } from './window-utils';

export interface IListItem {
name: string;
Expand Down Expand Up @@ -79,11 +80,98 @@ class PresenceStatus {
};

public setMyPresence = (myPresence: IPresenceStatus) => {
const currentPresenceStatus = presenceStatusStore.getStatus();
const count = presenceStatusStore.getNotificationCount();

presenceStatusStore.setStatus(myPresence.category);
if (currentPresenceStatus !== myPresence.category) {
presenceStatusStore.setStatus(myPresence.category);
this.updateSystemTrayPresence();
}
showBadgeCount(count);
showSystemTrayPresence(myPresence.category);
};

/**
* Shows the badge count
*
* @param count {number}
*/
public updateSystemTrayPresence = (): void => {
const status = presenceStatusStore.getStatus();
let tray = presenceStatusStore.getCurrentTray();
const backgroundImage = presenceStatusStore.generateImagePath(
status,
'tray',
);
if (!backgroundImage) {
return;
}
if (!tray) {
tray = initSysTray();
logger.info('presence-status-handler: create and save Symphony tray');
} else {
tray.setImage(backgroundImage);
logger.info('presence-status-handler: new Symphony status updated');
}
const currentStatus = presenceStatusStore.getStatus();
const presenceNamespace = 'PresenceStatus';
const isMana = !!windowHandler.isMana;
const contextMenu = Menu.buildFromTemplate([
{
label: i18n.t('My presence')(),
visible: isMana,
submenu: [
{
label: i18n.t(EPresenceStatus.AVAILABLE, presenceNamespace)(),
type: 'checkbox',
checked: currentStatus === EPresenceStatus.AVAILABLE,
click: () => {
this.handlePresenceChange(EPresenceStatus.AVAILABLE);
},
},
{
label: i18n.t(EPresenceStatus.BUSY, presenceNamespace)(),
type: 'checkbox',
checked: currentStatus === EPresenceStatus.BUSY,
click: () => {
this.handlePresenceChange(EPresenceStatus.BUSY);
},
},
{
label: i18n.t(EPresenceStatus.BE_RIGHT_BACK, presenceNamespace)(),
type: 'checkbox',
checked: currentStatus === EPresenceStatus.BE_RIGHT_BACK,
click: () => {
this.handlePresenceChange(EPresenceStatus.BE_RIGHT_BACK);
},
},
{
label: i18n.t(EPresenceStatus.OUT_OF_OFFICE, presenceNamespace)(),
type: 'checkbox',
checked: currentStatus === EPresenceStatus.OUT_OF_OFFICE,
click: () => {
this.handlePresenceChange(EPresenceStatus.OUT_OF_OFFICE);
},
},
],
},
{
label: i18n.t('Quit Symphony')(),
click: () => app.quit(),
},
]);
tray?.setContextMenu(contextMenu);
};

private handlePresenceChange = (currentStatus: EPresenceStatus) => {
const status = {
category: currentStatus,
statusGroup: '',
timestamp: Date.now(),
};
presenceStatus.setMyPresence(status);
const mainWebContents = windowHandler.getMainWebContents();
if (mainWebContents) {
mainWebContents.send('send-presence-status-data', currentStatus);
}
};

private setPresenceStatus = (
Expand All @@ -92,6 +180,7 @@ class PresenceStatus {
) => {
webContents.send('send-presence-status-data', status);
presenceStatusStore.setStatus(status);
this.updateSystemTrayPresence();
};
}

Expand Down
27 changes: 23 additions & 4 deletions src/app/stores/presence-status-store.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { Tray } from 'electron';
import { nativeTheme, Tray } from 'electron';
import * as path from 'path';
import {
EPresenceStatus,
IStatusBadge,
ITray,
} from '../../common/api-interface';
import { isMac, isWindowsOS } from '../../common/env';

// Flags can be read more here https://www.electronjs.org/docs/latest/api/browser-window#winsetthumbarbuttonsbuttons-windows

export class PresenceStatus {
private presenceStatus: IStatusBadge = {
status: EPresenceStatus.AVAILABLE,
status: EPresenceStatus.NO_PRESENCE,
count: 0,
};
private tray: ITray = {
Expand All @@ -37,9 +38,19 @@ export class PresenceStatus {

public getCurrentTray = () => this.tray.current;

public destroyCurrentTray = () => {
if (this.tray.current) {
this.tray.current.removeAllListeners();
this.tray.current.destroy();
this.tray.current = null;
}
};

public generateImagePath = (status: EPresenceStatus, place: string) => {
let backgroundImage: string = '';
const assetsPath = 'src/renderer/assets/presence-status';
const os = isWindowsOS ? 'windows' : isMac ? 'macOS' : 'linux';
const theme = nativeTheme.shouldUseDarkColors ? 'light' : 'dark';
const assetsPath = `src/renderer/assets/presence-status/${os}/${theme}`;

switch (status) {
case EPresenceStatus.AVAILABLE:
Expand Down Expand Up @@ -71,7 +82,15 @@ export class PresenceStatus {
place === 'tray' ? 'out-of-office-tray.png' : 'out-of-office.png'
}`;
break;

case EPresenceStatus.IN_A_MEETING:
backgroundImage = `../../../${assetsPath}/${
place === 'tray' ? 'in-a-meeting-tray.png' : 'in-a-meeting.png'
}`;
break;
case EPresenceStatus.NO_PRESENCE:
backgroundImage =
place === 'tray' ? `../../../${assetsPath}/no-status-tray.png` : '';
break;
default:
break;
}
Expand Down
9 changes: 9 additions & 0 deletions src/app/window-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
dialog,
Event,
ipcMain,
nativeTheme,
RenderProcessGoneDetails,
screen,
shell,
Expand Down Expand Up @@ -43,6 +44,7 @@ import {
import crashHandler from './crash-handler';
import LocalMenuShortcuts from './local-menu-shortcuts';
import { mainEvents } from './main-event-handler';
import { presenceStatus } from './presence-status-handler';
import { exportLogs } from './reports-handler';
import { SpellChecker } from './spell-check-handler';
import { winStore } from './stores';
Expand All @@ -60,6 +62,7 @@ import {
getWindowByName,
handleCertificateProxyVerification,
handleDownloadManager,
initSysTray,
injectStyles,
isSymphonyReachable,
loadBrowserViews,
Expand Down Expand Up @@ -455,6 +458,12 @@ export class WindowHandler {
mainEvents.publish('maximize');
}
this.mainWindow.show();
initSysTray();
if (isMac) {
nativeTheme.on('updated', () => {
presenceStatus.updateSystemTrayPresence();
});
}

// check for build expiry in case of test builds
this.checkExpiry(this.mainWindow);
Expand Down
43 changes: 25 additions & 18 deletions src/app/window-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import {
BrowserView,
BrowserWindow,
dialog,
Menu,
nativeImage,
nativeTheme,
Rectangle,
screen,
shell,
Expand Down Expand Up @@ -299,25 +301,30 @@ export const showBadgeCount = (count: number): void => {
};

/**
* Shows the badge count
*
* @param count {number}
* Creates sys tray
*/
export const showSystemTrayPresence = (status: EPresenceStatus): void => {
const tray = presenceStatusStore.getCurrentTray();
const backgroundImage = presenceStatusStore.generateImagePath(status, 'tray');
if (!backgroundImage) {
return;
}
if (!tray) {
const symphonyTray = new Tray(backgroundImage);
presenceStatusStore.setCurrentTray(symphonyTray);
symphonyTray.setToolTip('Symphony');
logger.info('main-api-handler: create and save Symphony tray');
} else {
tray.setImage(backgroundImage);
logger.info('main-api-handler: new Symphony status updated');
}
export const initSysTray = () => {
const defaultSysTrayIcon = 'no-status-tray.png';
const os = isWindowsOS ? 'windows' : isMac ? 'macOS' : 'linux';
const theme = nativeTheme.shouldUseDarkColors ? 'light' : 'dark';
logger.info('theme: ', theme, nativeTheme.themeSource);
const assetsPath = `renderer/assets/presence-status/${os}/${theme}`;
const defaultSysTrayIconPath = path.join(
__dirname,
`../${assetsPath}/${defaultSysTrayIcon}`,
);
const backgroundImage = nativeImage.createFromPath(defaultSysTrayIconPath);
const tray = new Tray(backgroundImage);
const contextMenu = Menu.buildFromTemplate([
{
label: i18n.t('Quit Symphony')(),
click: () => app.quit(),
},
]);
tray.setContextMenu(contextMenu);
tray.setToolTip('Symphony');
presenceStatusStore.setCurrentTray(tray);
return tray;
};

/**
Expand Down
1 change: 1 addition & 0 deletions src/common/api-interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ export enum EPresenceStatus {
'IN_A_MEETING' = 'IN_A_MEETING',
'BE_RIGHT_BACK' = 'BE_RIGHT_BACK',
'OFF_WORK' = 'OFF_WORK',
'NO_PRESENCE' = 'NO_PRESENCE',
}

export interface IPresenceStatus {
Expand Down
3 changes: 2 additions & 1 deletion src/locale/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -250,5 +250,6 @@
"AVAILABLE": "Available",
"OUT_OF_OFFICE": "Out of office",
"BE_RIGHT_BACK": "Be right back"
}
},
"My presence": "My presence"
}
3 changes: 2 additions & 1 deletion src/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -250,5 +250,6 @@
"AVAILABLE": "Available",
"OUT_OF_OFFICE": "Out of office",
"BE_RIGHT_BACK": "Be right back"
}
},
"My presence": "My presence"
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit f780a05

Please sign in to comment.