mirror of
https://github.com/finos/SymphonyElectron.git
synced 2025-02-25 18:55:29 -06:00
Typescript - Add logic to translate menu items
This commit is contained in:
@@ -9,17 +9,17 @@ const cacheCheckFilePath: string = path.join(app.getPath('userData'), 'CacheChec
|
||||
* Deletes app cache file if exists or clears
|
||||
* the cache for the session
|
||||
*/
|
||||
export async function cleanUpAppCache(): Promise<void> {
|
||||
export const cleanUpAppCache = async (): Promise<void> => {
|
||||
if (fs.existsSync(cacheCheckFilePath)) {
|
||||
await fs.unlinkSync(cacheCheckFilePath);
|
||||
return;
|
||||
}
|
||||
await new Promise((resolve) => session.defaultSession ? session.defaultSession.clearCache(resolve) : null);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new file cache file on app exit
|
||||
*/
|
||||
export function createAppCacheFile(): void {
|
||||
export const createAppCacheFile = (): void => {
|
||||
fs.writeFileSync(cacheCheckFilePath, '');
|
||||
}
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
import { app, Menu, session, shell } from 'electron';
|
||||
|
||||
import { isMac, isWindowsOS } from '../common/env';
|
||||
import { i18n } from '../common/i18n';
|
||||
import { i18n, LocaleType } from '../common/i18n';
|
||||
import { logger } from '../common/logger';
|
||||
import { autoLaunchInstance as autoLaunch } from './auto-launch-controller';
|
||||
import { config, IConfig } from './config-handler';
|
||||
@@ -55,9 +55,11 @@ const menuItemsArray = Object.keys(menuSections)
|
||||
export class AppMenu {
|
||||
private menu: Electron.Menu | undefined;
|
||||
private menuList: Electron.MenuItemConstructorOptions[];
|
||||
private readonly locale: LocaleType;
|
||||
|
||||
constructor() {
|
||||
this.menuList = [];
|
||||
this.locale = i18n.getLocale();
|
||||
this.buildMenu();
|
||||
}
|
||||
|
||||
@@ -77,6 +79,15 @@ export class AppMenu {
|
||||
Menu.setApplicationMenu(this.menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuilds the application menu on locale changes
|
||||
*
|
||||
* @param locale {LocaleType}
|
||||
*/
|
||||
public update(locale: LocaleType): void {
|
||||
if (this.locale !== locale) this.buildMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays popup application at the x,y coordinates
|
||||
*
|
||||
|
||||
@@ -7,7 +7,7 @@ import { config, IConfig } from './config-handler';
|
||||
/**
|
||||
* Sets chrome flags
|
||||
*/
|
||||
export default function setChromeFlags() {
|
||||
export const setChromeFlags = () => {
|
||||
const { customFlags } = config.getGlobalConfigFields([ 'customFlags' ]) as IConfig;
|
||||
|
||||
const configFlags: object = {
|
||||
@@ -63,4 +63,4 @@ export default function setChromeFlags() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,9 +1,10 @@
|
||||
import { BrowserWindow, ipcMain } from 'electron';
|
||||
|
||||
import { apiCmds, apiName, IApiArgs } from '../common/api-interface';
|
||||
import { LocaleType } from '../common/i18n';
|
||||
import { logger } from '../common/logger';
|
||||
import { windowHandler } from './window-handler';
|
||||
import { isValidWindow, setDataUrl, showBadgeCount } from './window-utils';
|
||||
import { isValidWindow, setDataUrl, showBadgeCount, updateLocale } from './window-utils';
|
||||
|
||||
/**
|
||||
* Handle API related ipc messages from renderers. Only messages from windows
|
||||
@@ -81,7 +82,7 @@ ipcMain.on(apiName.symphonyApi, (event: Electron.Event, arg: IApiArgs) => {
|
||||
case apiCmds.popupMenu: {
|
||||
const browserWin = BrowserWindow.fromWebContents(event.sender);
|
||||
if (browserWin && !browserWin.isDestroyed()) {
|
||||
const appMenu = windowHandler.getApplicationMenu();
|
||||
const appMenu = windowHandler.appMenu;
|
||||
if (appMenu) appMenu.popupMenu(browserWin);
|
||||
}
|
||||
break;
|
||||
@@ -100,14 +101,13 @@ ipcMain.on(apiName.symphonyApi, (event: Electron.Event, arg: IApiArgs) => {
|
||||
if (typeof arg.isInMeeting === 'boolean') {
|
||||
setIsInMeeting(arg.isInMeeting);
|
||||
}
|
||||
break;
|
||||
case ApiCmds.setLocale:
|
||||
break;*/
|
||||
case apiCmds.setLocale:
|
||||
if (typeof arg.locale === 'string') {
|
||||
let browserWin = electron.BrowserWindow.fromWebContents(event.sender);
|
||||
windowMgr.setLocale(browserWin, { language: arg.locale });
|
||||
updateLocale(arg.locale as LocaleType);
|
||||
}
|
||||
break;
|
||||
case ApiCmds.keyPress:
|
||||
/*case ApiCmds.keyPress:
|
||||
if (typeof arg.keyCode === 'number') {
|
||||
windowMgr.handleKeyPress(arg.keyCode);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { logger } from '../common/logger';
|
||||
import { getCommandLineArgs } from '../common/utils';
|
||||
import { cleanUpAppCache, createAppCacheFile } from './app-cache-handler';
|
||||
import { autoLaunchInstance } from './auto-launch-controller';
|
||||
import setChromeFlags from './chrome-flags';
|
||||
import { setChromeFlags } from './chrome-flags';
|
||||
import { config } from './config-handler';
|
||||
import './main-api-handler';
|
||||
import { windowHandler } from './window-handler';
|
||||
@@ -13,16 +13,10 @@ import { windowHandler } from './window-handler';
|
||||
const allowMultiInstance: string | boolean = getCommandLineArgs(process.argv, '--multiInstance', true) || isDevEnv;
|
||||
const singleInstanceLock: boolean = allowMultiInstance ? true : app.requestSingleInstanceLock();
|
||||
|
||||
if (!singleInstanceLock) {
|
||||
app.quit();
|
||||
} else {
|
||||
main();
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function that init the application
|
||||
*/
|
||||
async function main() {
|
||||
const main = async () => {
|
||||
await app.whenReady();
|
||||
createAppCacheFile();
|
||||
windowHandler.showLoadingScreen();
|
||||
@@ -42,6 +36,12 @@ async function main() {
|
||||
* Sets chrome flags from global config
|
||||
*/
|
||||
setChromeFlags();
|
||||
};
|
||||
|
||||
if (!singleInstanceLock) {
|
||||
app.quit();
|
||||
} else {
|
||||
main();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,7 @@ export class WindowHandler {
|
||||
/**
|
||||
* Main window opts
|
||||
*/
|
||||
private static getMainWindowOpts() {
|
||||
private static getMainWindowOpts(): ICustomBrowserWindowConstructorOpts {
|
||||
return {
|
||||
alwaysOnTop: false,
|
||||
frame: true,
|
||||
@@ -46,7 +46,7 @@ export class WindowHandler {
|
||||
/**
|
||||
* Loading window opts
|
||||
*/
|
||||
private static getLoadingWindowOpts() {
|
||||
private static getLoadingWindowOpts(): Electron.BrowserWindowConstructorOptions {
|
||||
return {
|
||||
alwaysOnTop: false,
|
||||
center: true,
|
||||
@@ -77,7 +77,9 @@ export class WindowHandler {
|
||||
return url.format(parsedUrl);
|
||||
}
|
||||
|
||||
private appMenu: AppMenu | null;
|
||||
public appMenu: AppMenu | null;
|
||||
public isAutoReload: boolean;
|
||||
|
||||
private readonly windowOpts: ICustomBrowserWindowConstructorOpts;
|
||||
private readonly globalConfig: IConfig;
|
||||
// Window reference
|
||||
@@ -86,7 +88,6 @@ export class WindowHandler {
|
||||
private loadingWindow: Electron.BrowserWindow | null;
|
||||
private aboutAppWindow: Electron.BrowserWindow | null;
|
||||
private moreInfoWindow: Electron.BrowserWindow | null;
|
||||
private isAutoReload: boolean;
|
||||
|
||||
constructor(opts?: Electron.BrowserViewConstructorOptions) {
|
||||
this.windows = {};
|
||||
@@ -129,6 +130,7 @@ export class WindowHandler {
|
||||
);
|
||||
this.mainWindow.webContents.send('initiate-custom-title-bar');
|
||||
}
|
||||
this.mainWindow.webContents.toggleDevTools();
|
||||
this.mainWindow.show();
|
||||
this.appMenu = new AppMenu();
|
||||
this.createAboutAppWindow();
|
||||
@@ -154,13 +156,6 @@ export class WindowHandler {
|
||||
return this.windows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the application menu
|
||||
*/
|
||||
public getApplicationMenu(): AppMenu | null {
|
||||
return this.appMenu;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets is auto reload when the application
|
||||
* is auto reloaded for optimizing memory
|
||||
@@ -171,15 +166,6 @@ export class WindowHandler {
|
||||
this.isAutoReload = shouldAutoReload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets is auto reload
|
||||
*
|
||||
* @return isAutoReload {boolean}
|
||||
*/
|
||||
public getIsAutoReload(): boolean {
|
||||
return this.isAutoReload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the window and a key has a window
|
||||
* @param key {string}
|
||||
|
||||
@@ -3,20 +3,37 @@ import * as path from 'path';
|
||||
import * as url from 'url';
|
||||
|
||||
import { isMac, isWindowsOS } from '../common/env';
|
||||
import { i18n, LocaleType } from '../common/i18n';
|
||||
import { logger } from '../common/logger';
|
||||
import { ICustomBrowserWindow, windowHandler } from './window-handler';
|
||||
|
||||
const checkValidWindow = true;
|
||||
|
||||
/**
|
||||
* Prevents window from navigating
|
||||
*
|
||||
* @param browserWindow
|
||||
*/
|
||||
export const preventWindowNavigation = (browserWindow: Electron.BrowserWindow): void => {
|
||||
const listener = (e: Electron.Event, winUrl: string) => {
|
||||
if (browserWindow.isDestroyed()
|
||||
|| browserWindow.webContents.isDestroyed()
|
||||
|| winUrl === browserWindow.webContents.getURL()) return;
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
browserWindow.webContents.on('will-navigate', listener);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates components windows
|
||||
*
|
||||
* @param componentName
|
||||
* @param opts
|
||||
*/
|
||||
export function createComponentWindow(
|
||||
export const createComponentWindow = (
|
||||
componentName: string,
|
||||
opts?: Electron.BrowserWindowConstructorOptions): BrowserWindow {
|
||||
opts?: Electron.BrowserWindowConstructorOptions): BrowserWindow => {
|
||||
|
||||
const parent = windowHandler.getMainWindow() || undefined;
|
||||
const options = {
|
||||
@@ -48,30 +65,14 @@ export function createComponentWindow(
|
||||
browserWindow.loadURL(targetUrl);
|
||||
preventWindowNavigation(browserWindow);
|
||||
return browserWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents window from navigating
|
||||
*
|
||||
* @param browserWindow
|
||||
*/
|
||||
export function preventWindowNavigation(browserWindow: Electron.BrowserWindow) {
|
||||
const listener = (e: Electron.Event, winUrl: string) => {
|
||||
if (browserWindow.isDestroyed()
|
||||
|| browserWindow.webContents.isDestroyed()
|
||||
|| winUrl === browserWindow.webContents.getURL()) return;
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
browserWindow.webContents.on('will-navigate', listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the badge count
|
||||
*
|
||||
* @param count {number}
|
||||
*/
|
||||
export function showBadgeCount(count: number): void {
|
||||
export const showBadgeCount = (count: number): void => {
|
||||
if (typeof count !== 'number') {
|
||||
logger.warn(`badgeCount: invalid func arg, must be a number: ${count}`);
|
||||
return;
|
||||
@@ -95,7 +96,7 @@ export function showBadgeCount(count: number): void {
|
||||
mainWindow.setOverlayIcon(null, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the data url
|
||||
@@ -103,7 +104,7 @@ export function showBadgeCount(count: number): void {
|
||||
* @param dataUrl
|
||||
* @param count
|
||||
*/
|
||||
export function setDataUrl(dataUrl: string, count: number): void {
|
||||
export const setDataUrl = (dataUrl: string, count: number): void => {
|
||||
const mainWindow = windowHandler.getMainWindow();
|
||||
if (mainWindow && dataUrl && count) {
|
||||
const img = nativeImage.createFromDataURL(dataUrl);
|
||||
@@ -111,30 +112,7 @@ export function setDataUrl(dataUrl: string, count: number): void {
|
||||
const desc = 'Symphony has ' + count + ' unread messages';
|
||||
mainWindow.setOverlayIcon(img, desc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets always on top property based on isAlwaysOnTop
|
||||
*
|
||||
* @param isAlwaysOnTop
|
||||
* @param shouldActivateMainWindow
|
||||
*/
|
||||
export function updateAlwaysOnTop(isAlwaysOnTop: boolean, shouldActivateMainWindow: boolean = true) {
|
||||
const browserWins: ICustomBrowserWindow[] = BrowserWindow.getAllWindows() as ICustomBrowserWindow[];
|
||||
if (browserWins.length > 0) {
|
||||
browserWins
|
||||
.filter((browser) => typeof browser.notificationObj !== 'object')
|
||||
.forEach((browser) => browser.setAlwaysOnTop(isAlwaysOnTop));
|
||||
|
||||
// An issue where changing the alwaysOnTop property
|
||||
// focus the pop-out window
|
||||
// Issue - Electron-209/470
|
||||
const mainWindow = windowHandler.getMainWindow();
|
||||
if (mainWindow && mainWindow.winName && shouldActivateMainWindow) {
|
||||
activate(mainWindow.winName);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Tries finding a window we have created with given name. If found, then
|
||||
@@ -145,10 +123,10 @@ export function updateAlwaysOnTop(isAlwaysOnTop: boolean, shouldActivateMainWind
|
||||
* @param {Boolean} shouldFocus whether to get window to focus or just show
|
||||
* without giving focus
|
||||
*/
|
||||
function activate(windowName: string, shouldFocus: boolean = true): void {
|
||||
export const activate = (windowName: string, shouldFocus: boolean = true): void => {
|
||||
|
||||
// Electron-136: don't activate when the app is reloaded programmatically
|
||||
if (windowHandler.getIsAutoReload()) return;
|
||||
if (windowHandler.isAutoReload) return;
|
||||
|
||||
const windows = windowHandler.getAllWindows();
|
||||
for (const key in windows) {
|
||||
@@ -167,14 +145,38 @@ function activate(windowName: string, shouldFocus: boolean = true): void {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets always on top property based on isAlwaysOnTop
|
||||
*
|
||||
* @param isAlwaysOnTop
|
||||
* @param shouldActivateMainWindow
|
||||
*/
|
||||
export const updateAlwaysOnTop = (isAlwaysOnTop: boolean, shouldActivateMainWindow: boolean = true): void => {
|
||||
const browserWins: ICustomBrowserWindow[] = BrowserWindow.getAllWindows() as ICustomBrowserWindow[];
|
||||
if (browserWins.length > 0) {
|
||||
browserWins
|
||||
.filter((browser) => typeof browser.notificationObj !== 'object')
|
||||
.forEach((browser) => browser.setAlwaysOnTop(isAlwaysOnTop));
|
||||
|
||||
// An issue where changing the alwaysOnTop property
|
||||
// focus the pop-out window
|
||||
// Issue - Electron-209/470
|
||||
const mainWindow = windowHandler.getMainWindow();
|
||||
if (mainWindow && mainWindow.winName && shouldActivateMainWindow) {
|
||||
activate(mainWindow.winName);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Ensure events comes from a window that we have created.
|
||||
*
|
||||
* @param {BrowserWindow} browserWin node emitter event to be tested
|
||||
* @return {Boolean} returns true if exists otherwise false
|
||||
*/
|
||||
export function isValidWindow(browserWin: Electron.BrowserWindow): boolean {
|
||||
export const isValidWindow = (browserWin: Electron.BrowserWindow): boolean => {
|
||||
if (!checkValidWindow) {
|
||||
return true;
|
||||
}
|
||||
@@ -190,4 +192,16 @@ export function isValidWindow(browserWin: Electron.BrowserWindow): boolean {
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the locale and rebuilds the entire application menu
|
||||
*
|
||||
* @param locale {LocaleType}
|
||||
*/
|
||||
export const updateLocale = (locale: LocaleType): void => {
|
||||
// sets the new locale
|
||||
i18n.setLocale(locale);
|
||||
const appMenu = windowHandler.appMenu;
|
||||
if (appMenu) appMenu.update(locale);
|
||||
};
|
||||
@@ -1,42 +1,41 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
// import { logger } from './logger';
|
||||
import { formatString } from './utils';
|
||||
|
||||
const localeCodeRegex = /^([a-z]{2})-([A-Z]{2})$/;
|
||||
|
||||
export type localeType = 'en-US' | 'ja-JP';
|
||||
export type LocaleType = 'en-US' | 'ja-JP';
|
||||
|
||||
class Translation {
|
||||
private static translate = (value: string, resource) => resource[value];
|
||||
private locale: localeType = 'en-US';
|
||||
private locale: LocaleType = 'en-US';
|
||||
private loadedResource: object = {};
|
||||
|
||||
/**
|
||||
* Apply the locale for translation
|
||||
*
|
||||
* @param locale
|
||||
*/
|
||||
public setLocale(locale: localeType): void {
|
||||
public setLocale(locale: LocaleType): void {
|
||||
const localeMatch: string[] | null = locale.match(localeCodeRegex);
|
||||
if (!locale && (!localeMatch || localeMatch.length < 1)) {
|
||||
// logger.error(`Translation: invalid locale ${locale} found`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.locale = locale;
|
||||
// logger.info(`Translation: locale updated with ${locale}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current locale
|
||||
*/
|
||||
public getLocale(): localeType {
|
||||
public getLocale(): LocaleType {
|
||||
return this.locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* fetches and returns the translated value
|
||||
*
|
||||
* @param value {string}
|
||||
* @param data {object}
|
||||
*/
|
||||
@@ -50,9 +49,10 @@ class Translation {
|
||||
|
||||
/**
|
||||
* Reads the resources dir and returns the data
|
||||
*
|
||||
* @param locale
|
||||
*/
|
||||
public loadResource(locale: localeType): object | null {
|
||||
public loadResource(locale: LocaleType): object | null {
|
||||
const resourcePath = path.resolve(__dirname, '..', 'locale', `${locale}.json`);
|
||||
|
||||
if (!fs.existsSync(resourcePath)) {
|
||||
|
||||
@@ -5,12 +5,10 @@ import AboutBox from './about-app';
|
||||
import LoadingScreen from './loading-screen';
|
||||
import MoreInfo from './more-info';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', load);
|
||||
|
||||
/**
|
||||
* Loads the appropriate component
|
||||
*/
|
||||
function load() {
|
||||
const load = () => {
|
||||
const query = new URL(window.location.href).searchParams;
|
||||
const componentName = query.get('componentName');
|
||||
|
||||
@@ -28,4 +26,6 @@ function load() {
|
||||
}
|
||||
const element = React.createElement(component);
|
||||
ReactDOM.render(element, document.getElementById('Root'));
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('DOMContentLoaded', load);
|
||||
@@ -5,12 +5,10 @@ import * as ReactDOM from 'react-dom';
|
||||
import WindowsTitleBar from '../renderer/windows-title-bar';
|
||||
import { SSFApi } from './ssf-api';
|
||||
|
||||
createAPI();
|
||||
|
||||
/**
|
||||
* creates API exposed from electron.
|
||||
*/
|
||||
function createAPI() {
|
||||
const createAPI = () => {
|
||||
// iframes (and any other non-top level frames) get no api access
|
||||
// http://stackoverflow.com/questions/326069/how-to-identify-if-a-webpage-is-being-loaded-inside-an-iframe-or-directly-into-t/326076
|
||||
if (window.self !== window.top) {
|
||||
@@ -26,7 +24,9 @@ function createAPI() {
|
||||
//
|
||||
// @ts-ignore
|
||||
window.ssf = new SSFApi();
|
||||
}
|
||||
};
|
||||
|
||||
createAPI();
|
||||
|
||||
// When the window is completely loaded
|
||||
ipcRenderer.on('page-load', () => {
|
||||
|
||||
@@ -8,15 +8,20 @@ const local = {
|
||||
};
|
||||
|
||||
// Throttle func
|
||||
const throttleSetBadgeCount = throttle((count) => {
|
||||
console.log(count);
|
||||
console.log(apiCmds.setBadgeCount);
|
||||
const throttledSetBadgeCount = throttle((count) => {
|
||||
local.ipcRenderer.send(apiName.symphonyApi, {
|
||||
cmd: apiCmds.setBadgeCount,
|
||||
count,
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
const throttledSetLocale = throttle((locale) => {
|
||||
local.ipcRenderer.send(apiName.symphonyApi, {
|
||||
cmd: apiCmds.setLocale,
|
||||
locale,
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
export class SSFApi {
|
||||
/**
|
||||
* sets the count on the tray icon to the given number.
|
||||
@@ -26,7 +31,18 @@ export class SSFApi {
|
||||
* note: for windws the number displayed will be 1 to 99 and 99+
|
||||
*/
|
||||
public setBadgeCount(count: number): void {
|
||||
throttleSetBadgeCount(count);
|
||||
throttledSetBadgeCount(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the language which updates the application locale
|
||||
* @param {string} locale - language identifier and a region identifier
|
||||
* Ex: en-US, ja-JP
|
||||
*/
|
||||
public setLocale(locale): void {
|
||||
if (typeof locale === 'string') {
|
||||
throttledSetLocale(locale);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"no-unused-expression": true,
|
||||
"no-use-before-declare": true,
|
||||
"no-var-requires": true,
|
||||
"only-arrow-functions": true,
|
||||
"no-console": [
|
||||
false,
|
||||
"log",
|
||||
|
||||
Reference in New Issue
Block a user