SDA-4070 Presence status on macOS (#1738)

* SDA-4070 Presence status on macOS

* bugfix

* SDA-4070 Presence status on macOS

* SDA-4070 Presence status on macOS
This commit is contained in:
Salah Benmoussati 2023-03-01 10:53:56 +01:00 committed by GitHub
parent a3e2192d4f
commit 722a47769e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 158 additions and 29 deletions

View File

@ -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
@ -171,6 +172,7 @@ app.on('window-all-closed', () => {
*/
app.on('quit', () => {
logger.info(`main: quitting the app!`);
presenceStatusStore.destroyCurrentTray();
cleanUpAppCache();
});

View File

@ -1,4 +1,4 @@
import { nativeImage, WebContents } from 'electron';
import { app, Menu, nativeImage, WebContents } from 'electron';
import {
EPresenceStatus,
IPresenceStatus,
@ -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;
@ -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 = (
@ -92,6 +180,7 @@ class PresenceStatus {
) => {
webContents.send('send-presence-status-data', status);
presenceStatusStore.setStatus(status);
this.updateSystemTrayPresence();
};
}

View File

@ -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 = {
@ -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:
@ -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;
}

View File

@ -9,6 +9,7 @@ import {
dialog,
Event,
ipcMain,
nativeTheme,
RenderProcessGoneDetails,
screen,
shell,
@ -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';
@ -60,6 +62,7 @@ import {
getWindowByName,
handleCertificateProxyVerification,
handleDownloadManager,
initSysTray,
injectStyles,
isSymphonyReachable,
loadBrowserViews,
@ -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);

View File

@ -3,7 +3,9 @@ import {
BrowserView,
BrowserWindow,
dialog,
Menu,
nativeImage,
nativeTheme,
Rectangle,
screen,
shell,
@ -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;
};
/**

View File

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

View File

@ -250,5 +250,6 @@
"AVAILABLE": "Available",
"OUT_OF_OFFICE": "Out of office",
"BE_RIGHT_BACK": "Be right back"
}
},
"My presence": "My presence"
}

View File

@ -250,5 +250,6 @@
"AVAILABLE": "Available",
"OUT_OF_OFFICE": "Out of office",
"BE_RIGHT_BACK": "Be right back"
}
},
"My presence": "My presence"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 990 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Before

Width:  |  Height:  |  Size: 412 B

After

Width:  |  Height:  |  Size: 412 B

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Before

Width:  |  Height:  |  Size: 395 B

After

Width:  |  Height:  |  Size: 395 B

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Before

Width:  |  Height:  |  Size: 515 B

After

Width:  |  Height:  |  Size: 515 B

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Before

Width:  |  Height:  |  Size: 432 B

After

Width:  |  Height:  |  Size: 432 B

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Before

Width:  |  Height:  |  Size: 527 B

After

Width:  |  Height:  |  Size: 527 B

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Before

Width:  |  Height:  |  Size: 515 B

After

Width:  |  Height:  |  Size: 515 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 515 B