mirror of
https://github.com/finos/SymphonyElectron.git
synced 2025-02-25 18:55:29 -06:00
Typescript - Completed window actions & snackbar, Updated i18n module
This commit is contained in:
parent
7771ea5b41
commit
3f799f10cc
@ -55,7 +55,7 @@ const menuItemsArray = Object.keys(menuSections)
|
|||||||
export class AppMenu {
|
export class AppMenu {
|
||||||
private menu: Electron.Menu | undefined;
|
private menu: Electron.Menu | undefined;
|
||||||
private menuList: Electron.MenuItemConstructorOptions[];
|
private menuList: Electron.MenuItemConstructorOptions[];
|
||||||
private readonly locale: LocaleType;
|
private locale: LocaleType;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.menuList = [];
|
this.menuList = [];
|
||||||
@ -85,7 +85,10 @@ export class AppMenu {
|
|||||||
* @param locale {LocaleType}
|
* @param locale {LocaleType}
|
||||||
*/
|
*/
|
||||||
public update(locale: LocaleType): void {
|
public update(locale: LocaleType): void {
|
||||||
if (this.locale !== locale) this.buildMenu();
|
if (this.locale !== locale) {
|
||||||
|
this.buildMenu();
|
||||||
|
this.locale = locale;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -131,15 +134,15 @@ export class AppMenu {
|
|||||||
id: menuSections.about,
|
id: menuSections.about,
|
||||||
label: app.getName(),
|
label: app.getName(),
|
||||||
submenu: [
|
submenu: [
|
||||||
{ label: i18n.t('About Symphony'), role: 'about' },
|
{ label: i18n.t('About Symphony')(), role: 'about' },
|
||||||
this.buildSeparator(),
|
this.buildSeparator(),
|
||||||
{ label: i18n.t('Services'), role: 'services' },
|
{ label: i18n.t('Services')(), role: 'services' },
|
||||||
this.buildSeparator(),
|
this.buildSeparator(),
|
||||||
{ label: i18n.t('Hide Symphony'), role: 'hide' },
|
{ label: i18n.t('Hide Symphony')(), role: 'hide' },
|
||||||
{ label: i18n.t('Hide Others'), role: 'hideothers' },
|
{ label: i18n.t('Hide Others')(), role: 'hideothers' },
|
||||||
{ label: i18n.t('Show All'), role: 'unhide' },
|
{ label: i18n.t('Show All')(), role: 'unhide' },
|
||||||
this.buildSeparator(),
|
this.buildSeparator(),
|
||||||
{ label: i18n.t('Quit Symphony'), role: 'quit' },
|
{ label: i18n.t('Quit Symphony')(), role: 'quit' },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -149,27 +152,27 @@ export class AppMenu {
|
|||||||
*/
|
*/
|
||||||
private buildEditMenu(): Electron.MenuItemConstructorOptions {
|
private buildEditMenu(): Electron.MenuItemConstructorOptions {
|
||||||
const menu = {
|
const menu = {
|
||||||
label: i18n.t('Edit'),
|
label: i18n.t('Edit')(),
|
||||||
submenu:
|
submenu:
|
||||||
[
|
[
|
||||||
this.assignRoleOrLabel('undo', i18n.t('Undo')),
|
this.assignRoleOrLabel('undo', i18n.t('Undo')()),
|
||||||
this.assignRoleOrLabel('redo', i18n.t('Redo')),
|
this.assignRoleOrLabel('redo', i18n.t('Redo')()),
|
||||||
this.buildSeparator(),
|
this.buildSeparator(),
|
||||||
this.assignRoleOrLabel('cut', i18n.t('Cut')),
|
this.assignRoleOrLabel('cut', i18n.t('Cut')()),
|
||||||
this.assignRoleOrLabel('copy', i18n.t('Copy')),
|
this.assignRoleOrLabel('copy', i18n.t('Copy')()),
|
||||||
this.assignRoleOrLabel('paste', i18n.t('Paste')),
|
this.assignRoleOrLabel('paste', i18n.t('Paste')()),
|
||||||
this.assignRoleOrLabel('pasteandmatchstyle', i18n.t('Paste and Match Style')),
|
this.assignRoleOrLabel('pasteandmatchstyle', i18n.t('Paste and Match Style')()),
|
||||||
this.assignRoleOrLabel('delete', i18n.t('Delete')),
|
this.assignRoleOrLabel('delete', i18n.t('Delete')()),
|
||||||
this.assignRoleOrLabel('selectall', i18n.t('Select All')),
|
this.assignRoleOrLabel('selectall', i18n.t('Select All')()),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isMac) {
|
if (isMac) {
|
||||||
menu.submenu.push(this.buildSeparator(), {
|
menu.submenu.push(this.buildSeparator(), {
|
||||||
label: i18n.t('Speech'),
|
label: i18n.t('Speech')(),
|
||||||
submenu: [
|
submenu: [
|
||||||
{ label: i18n.t('Start Speaking'), role: 'startspeaking' },
|
{ label: i18n.t('Start Speaking')(), role: 'startspeaking' },
|
||||||
{ label: i18n.t('Stop Speaking'), role: 'stopspeaking' },
|
{ label: i18n.t('Stop Speaking')(), role: 'stopspeaking' },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -181,18 +184,18 @@ export class AppMenu {
|
|||||||
*/
|
*/
|
||||||
private buildViewMenu(): Electron.MenuItemConstructorOptions {
|
private buildViewMenu(): Electron.MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
label: i18n.t('View'),
|
label: i18n.t('View')(),
|
||||||
submenu: [ {
|
submenu: [ {
|
||||||
accelerator: 'CmdOrCtrl+R',
|
accelerator: 'CmdOrCtrl+R',
|
||||||
click: (_item, focusedWindow) => focusedWindow ? focusedWindow.reload() : null,
|
click: (_item, focusedWindow) => focusedWindow ? focusedWindow.reload() : null,
|
||||||
label: i18n.t('Reload'),
|
label: i18n.t('Reload')(),
|
||||||
},
|
},
|
||||||
this.buildSeparator(),
|
this.buildSeparator(),
|
||||||
this.assignRoleOrLabel('resetzoom', i18n.t('Actual Size')),
|
this.assignRoleOrLabel('resetzoom', i18n.t('Actual Size')()),
|
||||||
this.assignRoleOrLabel('zoomin', i18n.t('Zoom In')),
|
this.assignRoleOrLabel('zoomin', i18n.t('Zoom In')()),
|
||||||
this.assignRoleOrLabel('zoomout', i18n.t('Zoom Out')),
|
this.assignRoleOrLabel('zoomout', i18n.t('Zoom Out')()),
|
||||||
this.buildSeparator(),
|
this.buildSeparator(),
|
||||||
this.assignRoleOrLabel('togglefullscreen', i18n.t('Toggle Full Screen')),
|
this.assignRoleOrLabel('togglefullscreen', i18n.t('Toggle Full Screen')()),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -202,11 +205,11 @@ export class AppMenu {
|
|||||||
*/
|
*/
|
||||||
private buildWindowMenu(): Electron.MenuItemConstructorOptions {
|
private buildWindowMenu(): Electron.MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
label: i18n.t('Window'),
|
label: i18n.t('Window')(),
|
||||||
role: 'window',
|
role: 'window',
|
||||||
submenu: [
|
submenu: [
|
||||||
this.assignRoleOrLabel('minimize', i18n.t('Minimize')),
|
this.assignRoleOrLabel('minimize', i18n.t('Minimize')()),
|
||||||
this.assignRoleOrLabel('close', i18n.t('Close')),
|
this.assignRoleOrLabel('close', i18n.t('Close')()),
|
||||||
this.buildSeparator(),
|
this.buildSeparator(),
|
||||||
{
|
{
|
||||||
checked: launchOnStartup,
|
checked: launchOnStartup,
|
||||||
@ -217,49 +220,49 @@ export class AppMenu {
|
|||||||
await autoLaunch.disableAutoLaunch();
|
await autoLaunch.disableAutoLaunch();
|
||||||
}
|
}
|
||||||
launchOnStartup = item.checked;
|
launchOnStartup = item.checked;
|
||||||
config.updateUserConfig({ launchOnStartup });
|
await config.updateUserConfig({ launchOnStartup });
|
||||||
},
|
},
|
||||||
label: i18n.t('Auto Launch On Startup'),
|
label: i18n.t('Auto Launch On Startup')(),
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
checked: isAlwaysOnTop,
|
checked: isAlwaysOnTop,
|
||||||
click: (item) => {
|
click: async (item) => {
|
||||||
isAlwaysOnTop = item.checked;
|
isAlwaysOnTop = item.checked;
|
||||||
updateAlwaysOnTop(item.checked, true);
|
updateAlwaysOnTop(item.checked, true);
|
||||||
config.updateUserConfig({ alwaysOnTop: item.checked });
|
await config.updateUserConfig({ alwaysOnTop: item.checked });
|
||||||
},
|
},
|
||||||
label: i18n.t('Always on Top'),
|
label: i18n.t('Always on Top')(),
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
checked: minimizeOnClose,
|
checked: minimizeOnClose,
|
||||||
click: (item) => {
|
click: async (item) => {
|
||||||
minimizeOnClose = item.checked;
|
minimizeOnClose = item.checked;
|
||||||
config.updateUserConfig({ minimizeOnClose });
|
await config.updateUserConfig({ minimizeOnClose });
|
||||||
},
|
},
|
||||||
label: i18n.t('Minimize on Close'),
|
label: i18n.t('Minimize on Close')(),
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
checked: bringToFront,
|
checked: bringToFront,
|
||||||
click: (item) => {
|
click: async (item) => {
|
||||||
bringToFront = item.checked;
|
bringToFront = item.checked;
|
||||||
config.updateUserConfig({ bringToFront });
|
await config.updateUserConfig({ bringToFront });
|
||||||
},
|
},
|
||||||
label: isWindowsOS
|
label: isWindowsOS
|
||||||
? i18n.t('Flash Notification in Taskbar')
|
? i18n.t('Flash Notification in Taskbar')()
|
||||||
: i18n.t('Bring to Front on Notifications'),
|
: i18n.t('Bring to Front on Notifications')(),
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
},
|
},
|
||||||
this.buildSeparator(),
|
this.buildSeparator(),
|
||||||
{
|
{
|
||||||
checked: memoryRefresh,
|
checked: memoryRefresh,
|
||||||
click: (item) => {
|
click: async (item) => {
|
||||||
memoryRefresh = item.checked;
|
memoryRefresh = item.checked;
|
||||||
config.updateUserConfig({ memoryRefresh });
|
await config.updateUserConfig({ memoryRefresh });
|
||||||
},
|
},
|
||||||
label: i18n.t('Refresh app when idle'),
|
label: i18n.t('Refresh app when idle')(),
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -273,7 +276,7 @@ export class AppMenu {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
label: i18n.t('Clear cache and Reload'),
|
label: i18n.t('Clear cache and Reload')(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@ -284,26 +287,26 @@ export class AppMenu {
|
|||||||
*/
|
*/
|
||||||
private buildHelpMenu(): Electron.MenuItemConstructorOptions {
|
private buildHelpMenu(): Electron.MenuItemConstructorOptions {
|
||||||
return {
|
return {
|
||||||
label: i18n.t('Help'),
|
label: i18n.t('Help')(),
|
||||||
role: 'help',
|
role: 'help',
|
||||||
submenu:
|
submenu:
|
||||||
[ {
|
[ {
|
||||||
click: () => shell.openExternal(i18n.t('Help Url')),
|
click: () => shell.openExternal(i18n.t('Help Url')()),
|
||||||
label: i18n.t('Symphony Help'),
|
label: i18n.t('Symphony Help')(),
|
||||||
}, {
|
}, {
|
||||||
click: () => shell.openExternal(i18n.t('Symphony Url')),
|
click: () => shell.openExternal(i18n.t('Symphony Url')()),
|
||||||
label: i18n.t('Learn More'),
|
label: i18n.t('Learn More')(),
|
||||||
}, {
|
}, {
|
||||||
label: i18n.t('Troubleshooting'),
|
label: i18n.t('Troubleshooting')(),
|
||||||
submenu: [ {
|
submenu: [ {
|
||||||
click: async () => exportLogs(),
|
click: async () => exportLogs(),
|
||||||
label: isMac ? i18n.t('Show Logs in Finder') : i18n.t('Show Logs in Explorer'),
|
label: isMac ? i18n.t('Show Logs in Finder')() : i18n.t('Show Logs in Explorer')(),
|
||||||
}, {
|
}, {
|
||||||
click: () => exportCrashDumps(),
|
click: () => exportCrashDumps(),
|
||||||
label: isMac ? i18n.t('Show crash dump in Finder') : i18n.t('Show crash dump in Explorer'),
|
label: isMac ? i18n.t('Show crash dump in Finder')() : i18n.t('Show crash dump in Explorer')(),
|
||||||
}, {
|
}, {
|
||||||
click: () => windowHandler.createMoreInfoWindow(),
|
click: () => windowHandler.createMoreInfoWindow(),
|
||||||
label: i18n.t('More Information'),
|
label: i18n.t('More Information')(),
|
||||||
} ],
|
} ],
|
||||||
} ],
|
} ],
|
||||||
};
|
};
|
||||||
|
@ -50,8 +50,8 @@ class AutoLaunchController extends AutoLaunch {
|
|||||||
logger.error(`auto-launch-controller: ${title}: failed to enable auto launch error: ${err}`);
|
logger.error(`auto-launch-controller: ${title}: failed to enable auto launch error: ${err}`);
|
||||||
if (focusedWindow && !focusedWindow.isDestroyed()) {
|
if (focusedWindow && !focusedWindow.isDestroyed()) {
|
||||||
dialog.showMessageBox(focusedWindow, {
|
dialog.showMessageBox(focusedWindow, {
|
||||||
message: i18n.t(title) + ': ' + err,
|
message: i18n.t(title)() + ': ' + err,
|
||||||
title: i18n.t(title),
|
title: i18n.t(title)(),
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -72,8 +72,8 @@ class AutoLaunchController extends AutoLaunch {
|
|||||||
logger.error(`auto-launch-controller: ${title}: failed to disable auto launch error: ${err}`);
|
logger.error(`auto-launch-controller: ${title}: failed to disable auto launch error: ${err}`);
|
||||||
if (focusedWindow && !focusedWindow.isDestroyed()) {
|
if (focusedWindow && !focusedWindow.isDestroyed()) {
|
||||||
dialog.showMessageBox(focusedWindow, {
|
dialog.showMessageBox(focusedWindow, {
|
||||||
message: i18n.t(title) + ': ' + err,
|
message: i18n.t(title)() + ': ' + err,
|
||||||
title: i18n.t(title),
|
title: i18n.t(title)(),
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
import { app } from 'electron';
|
import { app, dialog } from 'electron';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
|
import * as util from 'util';
|
||||||
import { isDevEnv, isMac } from '../common/env';
|
import { isDevEnv, isMac } from '../common/env';
|
||||||
import { logger } from '../common/logger';
|
import { logger } from '../common/logger';
|
||||||
import { compareVersions, pick } from '../common/utils';
|
import { compareVersions, pick } from '../common/utils';
|
||||||
|
|
||||||
|
const writeFile = util.promisify(fs.writeFile);
|
||||||
|
|
||||||
const ignoreSettings = [
|
const ignoreSettings = [
|
||||||
'minimizeOnClose',
|
'minimizeOnClose',
|
||||||
'launchOnStartup',
|
'launchOnStartup',
|
||||||
@ -34,6 +37,7 @@ export interface IConfig {
|
|||||||
permissions: IPermission;
|
permissions: IPermission;
|
||||||
customFlags: ICustomFlag;
|
customFlags: ICustomFlag;
|
||||||
crashReporter: ICrashReporter;
|
crashReporter: ICrashReporter;
|
||||||
|
mainWinPos: ICustomRectangle;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IPermission {
|
export interface IPermission {
|
||||||
@ -63,6 +67,11 @@ export interface INotificationSetting {
|
|||||||
display: string;
|
display: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ICustomRectangle extends Electron.Rectangle {
|
||||||
|
isMaximized: boolean;
|
||||||
|
isFullScreen: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
class Config {
|
class Config {
|
||||||
private userConfig: IConfig | {};
|
private userConfig: IConfig | {};
|
||||||
private globalConfig: IConfig | {};
|
private globalConfig: IConfig | {};
|
||||||
@ -121,9 +130,14 @@ class Config {
|
|||||||
*
|
*
|
||||||
* @param data {IConfig}
|
* @param data {IConfig}
|
||||||
*/
|
*/
|
||||||
public updateUserConfig(data: Partial<IConfig>): void {
|
public async updateUserConfig(data: Partial<IConfig>): Promise<void> {
|
||||||
this.userConfig = { ...this.userConfig, ...data };
|
this.userConfig = { ...this.userConfig, ...data };
|
||||||
fs.writeFileSync(this.userConfigPath, JSON.stringify(this.userConfig), { encoding: 'utf8' });
|
try {
|
||||||
|
await writeFile(this.userConfigPath, JSON.stringify(this.userConfig), { encoding: 'utf8' });
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`config-handler: failed to update user config file with ${data}`, error);
|
||||||
|
dialog.showErrorBox('Error updating user config file', 'failed to write user config file with ${}');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -167,9 +181,9 @@ class Config {
|
|||||||
* If user config doesn't exits?
|
* If user config doesn't exits?
|
||||||
* this creates a new one with { configVersion: current_app_version }
|
* this creates a new one with { configVersion: current_app_version }
|
||||||
*/
|
*/
|
||||||
private readUserConfig() {
|
private async readUserConfig() {
|
||||||
if (!fs.existsSync(this.userConfigPath)) {
|
if (!fs.existsSync(this.userConfigPath)) {
|
||||||
this.updateUserConfig({ configVersion: app.getVersion().toString() } as IConfig);
|
await this.updateUserConfig({ configVersion: app.getVersion().toString() } as IConfig);
|
||||||
}
|
}
|
||||||
this.userConfig = this.parseConfigData(fs.readFileSync(this.userConfigPath, 'utf8'));
|
this.userConfig = this.parseConfigData(fs.readFileSync(this.userConfigPath, 'utf8'));
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,6 @@ ipcMain.on(apiName.symphonyApi, (event: Electron.Event, arg: IApiArgs) => {
|
|||||||
break;*/
|
break;*/
|
||||||
case apiCmds.setBadgeCount:
|
case apiCmds.setBadgeCount:
|
||||||
if (typeof arg.count === 'number') {
|
if (typeof arg.count === 'number') {
|
||||||
console.log(arg.count);
|
|
||||||
showBadgeCount(arg.count);
|
showBadgeCount(arg.count);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -17,8 +17,8 @@ export const exportLogs = (): void => {
|
|||||||
|
|
||||||
if (!fs.existsSync(source) && focusedWindow && !focusedWindow.isDestroyed()) {
|
if (!fs.existsSync(source) && focusedWindow && !focusedWindow.isDestroyed()) {
|
||||||
dialog.showMessageBox(focusedWindow, {
|
dialog.showMessageBox(focusedWindow, {
|
||||||
message: i18n.t('No logs are available to share'),
|
message: i18n.t('No logs are available to share')(),
|
||||||
title: i18n.t('Failed!'),
|
title: i18n.t('Failed!')(),
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@ -34,8 +34,8 @@ export const exportLogs = (): void => {
|
|||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (focusedWindow && !focusedWindow.isDestroyed()) {
|
if (focusedWindow && !focusedWindow.isDestroyed()) {
|
||||||
dialog.showMessageBox(focusedWindow, {
|
dialog.showMessageBox(focusedWindow, {
|
||||||
message: `${i18n.t('Unable to generate logs due to ')} ${err}`,
|
message: `${i18n.t('Unable to generate logs due to ')()} ${err}`,
|
||||||
title: i18n.t('Failed!'),
|
title: i18n.t('Failed!')(),
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -50,8 +50,8 @@ export const exportCrashDumps = (): void => {
|
|||||||
|
|
||||||
if (!fs.existsSync(source) || fs.readdirSync(source).length === 0 && focusedWindow && !focusedWindow.isDestroyed()) {
|
if (!fs.existsSync(source) || fs.readdirSync(source).length === 0 && focusedWindow && !focusedWindow.isDestroyed()) {
|
||||||
electron.dialog.showMessageBox(focusedWindow as BrowserWindow, {
|
electron.dialog.showMessageBox(focusedWindow as BrowserWindow, {
|
||||||
message: i18n.t('No crashes available to share'),
|
message: i18n.t('No crashes available to share')(),
|
||||||
title: i18n.t('Failed!'),
|
title: i18n.t('Failed!')(),
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@ -69,8 +69,8 @@ export const exportCrashDumps = (): void => {
|
|||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
if (focusedWindow && !focusedWindow.isDestroyed()) {
|
if (focusedWindow && !focusedWindow.isDestroyed()) {
|
||||||
electron.dialog.showMessageBox(focusedWindow, {
|
electron.dialog.showMessageBox(focusedWindow, {
|
||||||
message: `${i18n.t('Unable to generate crash reports due to ')} ${err}`,
|
message: `${i18n.t('Unable to generate crash reports due to ')()} ${err}`,
|
||||||
title: i18n.t('Failed!'),
|
title: i18n.t('Failed!')(),
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ class ScreenSnippet {
|
|||||||
if (this.child) this.child.kill();
|
if (this.child) this.child.kill();
|
||||||
try {
|
try {
|
||||||
await this.execCmd(this.captureUtil, this.captureUtilArgs);
|
await this.execCmd(this.captureUtil, this.captureUtilArgs);
|
||||||
const { message, data, type } = await this.convertFileToData();
|
const { message, data, type }: IScreenSnippet = await this.convertFileToData();
|
||||||
webContents.send('screen-snippet-data', { message, data, type });
|
webContents.send('screen-snippet-data', { message, data, type });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`screen-snippet: screen snippet process was killed`, error);
|
logger.error(`screen-snippet: screen snippet process was killed`, error);
|
||||||
|
40
src/browser/window-actions.ts
Normal file
40
src/browser/window-actions.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { BrowserWindow } from 'electron';
|
||||||
|
|
||||||
|
import { throttle } from '../common/utils';
|
||||||
|
import { config } from './config-handler';
|
||||||
|
import { ICustomBrowserWindow } from './window-handler';
|
||||||
|
|
||||||
|
export const saveWindowSettings = (): void => {
|
||||||
|
const browserWindow = BrowserWindow.getFocusedWindow() as ICustomBrowserWindow;
|
||||||
|
|
||||||
|
if (browserWindow && !browserWindow.isDestroyed()) {
|
||||||
|
const [ x, y ] = browserWindow.getPosition();
|
||||||
|
const [ width, height ] = browserWindow.getSize();
|
||||||
|
if (x && y && width && height) {
|
||||||
|
browserWindow.webContents.send('boundChanges', { x, y, width, height });
|
||||||
|
|
||||||
|
if (browserWindow.winName === 'main') {
|
||||||
|
const isMaximized = browserWindow.isMaximized();
|
||||||
|
const isFullScreen = browserWindow.isFullScreen();
|
||||||
|
config.updateUserConfig({ mainWinPos: { x, y, width, height, isMaximized, isFullScreen } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export const enterFullScreen = () => {
|
||||||
|
const browserWindow = BrowserWindow.getFocusedWindow() as ICustomBrowserWindow;
|
||||||
|
if (browserWindow && !browserWindow.isDestroyed() && browserWindow.winName === 'main') {
|
||||||
|
browserWindow.webContents.send('window-enter-full-screen');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const leaveFullScreen = () => {
|
||||||
|
const browserWindow = BrowserWindow.getFocusedWindow() as ICustomBrowserWindow;
|
||||||
|
if (browserWindow && !browserWindow.isDestroyed() && browserWindow.winName === 'main') {
|
||||||
|
browserWindow.webContents.send('window-leave-full-screen');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const throttledWindowChanges = throttle(saveWindowSettings, 1000);
|
@ -1,16 +1,17 @@
|
|||||||
|
import * as electron from 'electron';
|
||||||
import { BrowserWindow, crashReporter } from 'electron';
|
import { BrowserWindow, crashReporter } from 'electron';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
|
|
||||||
|
import { buildNumber, clientVersion, version } from '../../package.json';
|
||||||
import { isWindowsOS } from '../common/env';
|
import { isWindowsOS } from '../common/env';
|
||||||
import { getCommandLineArgs, getGuid } from '../common/utils';
|
import { getCommandLineArgs, getGuid } from '../common/utils';
|
||||||
import { AppMenu } from './app-menu';
|
import { AppMenu } from './app-menu';
|
||||||
import { config, IConfig } from './config-handler';
|
import { config, IConfig } from './config-handler';
|
||||||
|
import { enterFullScreen, leaveFullScreen, throttledWindowChanges } from './window-actions';
|
||||||
import { createComponentWindow } from './window-utils';
|
import { createComponentWindow } from './window-utils';
|
||||||
|
|
||||||
const { buildNumber, clientVersion, version } = require('../../package.json'); // tslint:disable-line:no-var-requires
|
|
||||||
|
|
||||||
interface ICustomBrowserWindowConstructorOpts extends Electron.BrowserWindowConstructorOptions {
|
interface ICustomBrowserWindowConstructorOpts extends Electron.BrowserWindowConstructorOptions {
|
||||||
winKey: string;
|
winKey: string;
|
||||||
}
|
}
|
||||||
@ -20,28 +21,11 @@ export interface ICustomBrowserWindow extends Electron.BrowserWindow {
|
|||||||
notificationObj?: object;
|
notificationObj?: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WindowHandler {
|
// Default window width & height
|
||||||
|
const DEFAULT_WIDTH: number = 900;
|
||||||
|
const DEFAULT_HEIGHT: number = 900;
|
||||||
|
|
||||||
/**
|
export class WindowHandler {
|
||||||
* Main window opts
|
|
||||||
*/
|
|
||||||
private static getMainWindowOpts(): ICustomBrowserWindowConstructorOpts {
|
|
||||||
return {
|
|
||||||
alwaysOnTop: false,
|
|
||||||
frame: true,
|
|
||||||
minHeight: 300,
|
|
||||||
minWidth: 400,
|
|
||||||
show: false,
|
|
||||||
title: 'Symphony',
|
|
||||||
webPreferences: {
|
|
||||||
nativeWindowOpen: true,
|
|
||||||
nodeIntegration: false,
|
|
||||||
preload: path.join(__dirname, '../renderer/preload-main'),
|
|
||||||
sandbox: false,
|
|
||||||
},
|
|
||||||
winKey: getGuid(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loading window opts
|
* Loading window opts
|
||||||
@ -82,6 +66,7 @@ export class WindowHandler {
|
|||||||
|
|
||||||
private readonly windowOpts: ICustomBrowserWindowConstructorOpts;
|
private readonly windowOpts: ICustomBrowserWindowConstructorOpts;
|
||||||
private readonly globalConfig: IConfig;
|
private readonly globalConfig: IConfig;
|
||||||
|
private readonly config: IConfig;
|
||||||
// Window reference
|
// Window reference
|
||||||
private readonly windows: object;
|
private readonly windows: object;
|
||||||
private mainWindow: ICustomBrowserWindow | null;
|
private mainWindow: ICustomBrowserWindow | null;
|
||||||
@ -90,8 +75,12 @@ export class WindowHandler {
|
|||||||
private moreInfoWindow: Electron.BrowserWindow | null;
|
private moreInfoWindow: Electron.BrowserWindow | null;
|
||||||
|
|
||||||
constructor(opts?: Electron.BrowserViewConstructorOptions) {
|
constructor(opts?: Electron.BrowserViewConstructorOptions) {
|
||||||
|
// Settings
|
||||||
|
this.config = config.getConfigFields([ 'isCustomTitleBar', 'mainWinPos' ]);
|
||||||
|
this.globalConfig = config.getGlobalConfigFields([ 'url', 'crashReporter' ]);
|
||||||
|
|
||||||
this.windows = {};
|
this.windows = {};
|
||||||
this.windowOpts = { ...WindowHandler.getMainWindowOpts(), ...opts };
|
this.windowOpts = { ...this.getMainWindowOpts(), ...opts };
|
||||||
this.isAutoReload = false;
|
this.isAutoReload = false;
|
||||||
this.appMenu = null;
|
this.appMenu = null;
|
||||||
// Window references
|
// Window references
|
||||||
@ -99,7 +88,6 @@ export class WindowHandler {
|
|||||||
this.loadingWindow = null;
|
this.loadingWindow = null;
|
||||||
this.aboutAppWindow = null;
|
this.aboutAppWindow = null;
|
||||||
this.moreInfoWindow = null;
|
this.moreInfoWindow = null;
|
||||||
this.globalConfig = config.getGlobalConfigFields([ 'url', 'crashReporter' ]);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const extra = { podUrl: this.globalConfig.url, process: 'main' };
|
const extra = { podUrl: this.globalConfig.url, process: 'main' };
|
||||||
@ -113,25 +101,46 @@ export class WindowHandler {
|
|||||||
* Starting point of the app
|
* Starting point of the app
|
||||||
*/
|
*/
|
||||||
public createApplication() {
|
public createApplication() {
|
||||||
this.mainWindow = new BrowserWindow(this.windowOpts) as ICustomBrowserWindow;
|
// set window opts with additional config
|
||||||
|
this.mainWindow = new BrowserWindow({
|
||||||
|
...this.windowOpts, ...this.getBounds(this.config.mainWinPos),
|
||||||
|
}) as ICustomBrowserWindow;
|
||||||
this.mainWindow.winName = 'main';
|
this.mainWindow.winName = 'main';
|
||||||
|
|
||||||
|
// 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()) {
|
||||||
|
this.mainWindow.setMenuBarVisibility(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const urlFromCmd = getCommandLineArgs(process.argv, '--url=', false);
|
const urlFromCmd = getCommandLineArgs(process.argv, '--url=', false);
|
||||||
this.mainWindow.loadURL(urlFromCmd && urlFromCmd.substr(6) || WindowHandler.validateURL(this.globalConfig.url));
|
this.mainWindow.loadURL(urlFromCmd && urlFromCmd.substr(6) || WindowHandler.validateURL(this.globalConfig.url));
|
||||||
this.mainWindow.webContents.on('did-finish-load', () => {
|
this.mainWindow.webContents.on('did-finish-load', () => {
|
||||||
|
// close the loading window when
|
||||||
|
// the main windows finished loading
|
||||||
if (this.loadingWindow) {
|
if (this.loadingWindow) {
|
||||||
this.loadingWindow.destroy();
|
this.loadingWindow.destroy();
|
||||||
this.loadingWindow = null;
|
this.loadingWindow = null;
|
||||||
}
|
}
|
||||||
if (!this.mainWindow) return;
|
// early exit if the window has already been destroyed
|
||||||
if (isWindowsOS && this.mainWindow && config.getConfigFields([ 'isCustomTitleBar' ])) {
|
if (!this.mainWindow || this.mainWindow.isDestroyed()) return;
|
||||||
|
|
||||||
|
// Injects custom title bar css into the webContents
|
||||||
|
if (isWindowsOS && this.mainWindow && this.config.isCustomTitleBar) {
|
||||||
this.mainWindow.webContents.insertCSS(
|
this.mainWindow.webContents.insertCSS(
|
||||||
fs.readFileSync(path.join(__dirname, '..', '/renderer/styles/title-bar.css'), 'utf8').toString(),
|
fs.readFileSync(path.join(__dirname, '..', '/renderer/styles/title-bar.css'), 'utf8').toString(),
|
||||||
);
|
);
|
||||||
this.mainWindow.webContents.send('initiate-custom-title-bar');
|
this.mainWindow.webContents.send('initiate-custom-title-bar');
|
||||||
}
|
}
|
||||||
this.mainWindow.show();
|
this.mainWindow.webContents.insertCSS(
|
||||||
|
fs.readFileSync(path.join(__dirname, '..', '/renderer/styles/snack-bar.css'), 'utf8').toString(),
|
||||||
|
);
|
||||||
|
this.mainWindow.webContents.send('page-load');
|
||||||
this.appMenu = new AppMenu();
|
this.appMenu = new AppMenu();
|
||||||
|
this.monitorWindowActions();
|
||||||
|
// Ready to show the window
|
||||||
|
this.mainWindow.show();
|
||||||
});
|
});
|
||||||
this.mainWindow.webContents.toggleDevTools();
|
this.mainWindow.webContents.toggleDevTools();
|
||||||
this.addWindow(this.windowOpts.winKey, this.mainWindow);
|
this.addWindow(this.windowOpts.winKey, this.mainWindow);
|
||||||
@ -171,7 +180,7 @@ export class WindowHandler {
|
|||||||
* @param window {Electron.BrowserWindow}
|
* @param window {Electron.BrowserWindow}
|
||||||
*/
|
*/
|
||||||
public hasWindow(key: string, window: Electron.BrowserWindow): boolean {
|
public hasWindow(key: string, window: Electron.BrowserWindow): boolean {
|
||||||
const browserWindow = this.windows[key];
|
const browserWindow = this.windows[ key ];
|
||||||
return browserWindow && window === browserWindow;
|
return browserWindow && window === browserWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,7 +229,68 @@ export class WindowHandler {
|
|||||||
* @param browserWindow {Electron.BrowserWindow}
|
* @param browserWindow {Electron.BrowserWindow}
|
||||||
*/
|
*/
|
||||||
private addWindow(key: string, browserWindow: Electron.BrowserWindow): void {
|
private addWindow(key: string, browserWindow: Electron.BrowserWindow): void {
|
||||||
this.windows[key] = browserWindow;
|
this.windows[ key ] = browserWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the main window bounds
|
||||||
|
*/
|
||||||
|
private monitorWindowActions(): void {
|
||||||
|
const eventNames = [ 'move', 'resize', 'maximize', 'unmaximize' ];
|
||||||
|
eventNames.forEach((event: string) => {
|
||||||
|
// @ts-ignore
|
||||||
|
if (this.mainWindow) this.mainWindow.on(event, throttledWindowChanges);
|
||||||
|
});
|
||||||
|
if (this.mainWindow) {
|
||||||
|
this.mainWindow.on('enter-full-screen', enterFullScreen);
|
||||||
|
this.mainWindow.on('leave-full-screen', leaveFullScreen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the config stored rectangle if it is contained within the workArea of at
|
||||||
|
* least one of the screens else returns the default rectangle value with out x, y
|
||||||
|
* as the default is to center the window
|
||||||
|
*
|
||||||
|
* @param mainWinPos {Electron.Rectangle}
|
||||||
|
* @return {x?: Number, y?: Number, width: Number, height: Number}
|
||||||
|
*/
|
||||||
|
private getBounds(mainWinPos): Partial<Electron.Rectangle> {
|
||||||
|
if (!mainWinPos) return { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT };
|
||||||
|
const displays = electron.screen.getAllDisplays();
|
||||||
|
|
||||||
|
for (let i = 0, len = displays.length; i < len; i++) {
|
||||||
|
const workArea = displays[ i ].workArea;
|
||||||
|
if (mainWinPos.x >= workArea.x && mainWinPos.y >= workArea.y &&
|
||||||
|
((mainWinPos.x + mainWinPos.width) <= (workArea.x + workArea.width)) &&
|
||||||
|
((mainWinPos.y + mainWinPos.height) <= (workArea.y + workArea.height))) {
|
||||||
|
return mainWinPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main window opts
|
||||||
|
*/
|
||||||
|
private getMainWindowOpts(): ICustomBrowserWindowConstructorOpts {
|
||||||
|
// config fields
|
||||||
|
const { isCustomTitleBar } = this.config;
|
||||||
|
return {
|
||||||
|
alwaysOnTop: false,
|
||||||
|
frame: !(isCustomTitleBar && isWindowsOS),
|
||||||
|
minHeight: 300,
|
||||||
|
minWidth: 300,
|
||||||
|
show: false,
|
||||||
|
title: 'Symphony',
|
||||||
|
webPreferences: {
|
||||||
|
nativeWindowOpen: true,
|
||||||
|
nodeIntegration: false,
|
||||||
|
preload: path.join(__dirname, '../renderer/preload-main'),
|
||||||
|
sandbox: false,
|
||||||
|
},
|
||||||
|
winKey: getGuid(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,8 +7,20 @@ const localeCodeRegex = /^([a-z]{2})-([A-Z]{2})$/;
|
|||||||
|
|
||||||
export type LocaleType = 'en-US' | 'ja-JP';
|
export type LocaleType = 'en-US' | 'ja-JP';
|
||||||
|
|
||||||
|
type formaterFunction = (...args: any[]) => string;
|
||||||
|
|
||||||
class Translation {
|
class Translation {
|
||||||
private static translate = (value: string, resource) => resource[value];
|
/**
|
||||||
|
* Returns translated string with respect to value, resource & name space
|
||||||
|
*
|
||||||
|
* @param value {string} key field in the resources
|
||||||
|
* @param resource {string} current locale resource
|
||||||
|
* @param namespace {string} name space in the resource
|
||||||
|
*/
|
||||||
|
private static translate(value: string, resource: JSON | null, namespace: string | undefined): string {
|
||||||
|
return resource ? Translation.getResource(resource, namespace)[value] : null;
|
||||||
|
}
|
||||||
|
private static getResource = (resource: JSON, namespace: string | undefined): JSON => namespace ? resource[namespace] : resource;
|
||||||
private locale: LocaleType = 'en-US';
|
private locale: LocaleType = 'en-US';
|
||||||
private loadedResource: object = {};
|
private loadedResource: object = {};
|
||||||
|
|
||||||
@ -37,14 +49,18 @@ class Translation {
|
|||||||
* fetches and returns the translated value
|
* fetches and returns the translated value
|
||||||
*
|
*
|
||||||
* @param value {string}
|
* @param value {string}
|
||||||
* @param data {object}
|
* @param namespace {string}
|
||||||
|
* @example t('translate and formats {data} ', namespace)({ data: 'string' })
|
||||||
|
* @returns translate and formats string
|
||||||
*/
|
*/
|
||||||
public t(value: string, data?: object): string {
|
public t(value: string, namespace?: string): formaterFunction {
|
||||||
if (this.loadedResource && this.loadedResource[this.locale]) {
|
return (...args: any[]): string => {
|
||||||
return formatString(Translation.translate(value, this.loadedResource[this.locale]));
|
if (this.loadedResource && this.loadedResource[this.locale]) {
|
||||||
}
|
return formatString(Translation.translate(value, this.loadedResource[this.locale], namespace), args);
|
||||||
const resource = this.loadResource(this.locale);
|
}
|
||||||
return formatString(resource ? resource[value] : value || value, data);
|
const resource = this.loadResource(this.locale);
|
||||||
|
return formatString(Translation.translate(value, resource, namespace) || value, args);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,11 +68,10 @@ class Translation {
|
|||||||
*
|
*
|
||||||
* @param locale
|
* @param locale
|
||||||
*/
|
*/
|
||||||
public loadResource(locale: LocaleType): object | null {
|
public loadResource(locale: LocaleType): JSON | null {
|
||||||
const resourcePath = path.resolve(__dirname, '..', 'locale', `${locale}.json`);
|
const resourcePath = path.resolve(__dirname, '..', 'locale', `${locale}.json`);
|
||||||
|
|
||||||
if (!fs.existsSync(resourcePath)) {
|
if (!fs.existsSync(resourcePath)) {
|
||||||
// logger.error(`Translation: locale resource path does not exits ${resourcePath}`);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,9 @@ export default class AboutApp extends React.Component<{}, IState> {
|
|||||||
clientVersion: '0',
|
clientVersion: '0',
|
||||||
version: 'N/A',
|
version: 'N/A',
|
||||||
};
|
};
|
||||||
|
this.updateState = this.updateState.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* main render function
|
* main render function
|
||||||
*/
|
*/
|
||||||
@ -42,7 +44,7 @@ export default class AboutApp extends React.Component<{}, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
ipcRenderer.on('about-app-data', this.updateState.bind(this));
|
ipcRenderer.on('about-app-data', this.updateState);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
@ -51,6 +53,7 @@ export default class AboutApp extends React.Component<{}, IState> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the About app state
|
* Sets the About app state
|
||||||
|
*
|
||||||
* @param _event
|
* @param _event
|
||||||
* @param data {Object} { buildNumber, clientVersion, version }
|
* @param data {Object} { buildNumber, clientVersion, version }
|
||||||
*/
|
*/
|
||||||
|
@ -3,6 +3,7 @@ import * as React from 'react';
|
|||||||
import * as ReactDOM from 'react-dom';
|
import * as ReactDOM from 'react-dom';
|
||||||
|
|
||||||
import WindowsTitleBar from '../renderer/windows-title-bar';
|
import WindowsTitleBar from '../renderer/windows-title-bar';
|
||||||
|
import SnackBar from './snack-bar';
|
||||||
import { SSFApi } from './ssf-api';
|
import { SSFApi } from './ssf-api';
|
||||||
|
|
||||||
interface ISSFWindow extends Window {
|
interface ISSFWindow extends Window {
|
||||||
@ -37,8 +38,13 @@ createAPI();
|
|||||||
|
|
||||||
// When the window is completely loaded
|
// When the window is completely loaded
|
||||||
ipcRenderer.on('page-load', () => {
|
ipcRenderer.on('page-load', () => {
|
||||||
const element = React.createElement(WindowsTitleBar);
|
// injects custom window title bar
|
||||||
ReactDOM.render(element, document.body);
|
const titleBar = React.createElement(WindowsTitleBar);
|
||||||
|
ReactDOM.render(titleBar, document.body.appendChild(document.createElement( 'div' )));
|
||||||
|
|
||||||
|
// injects snack bar
|
||||||
|
const snackBar = React.createElement(SnackBar);
|
||||||
|
ReactDOM.render(snackBar, document.body.appendChild(document.createElement( 'div' )));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Creates a custom tile bar for Windows
|
// Creates a custom tile bar for Windows
|
||||||
|
69
src/renderer/snack-bar.tsx
Normal file
69
src/renderer/snack-bar.tsx
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { ipcRenderer } from 'electron';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import Timer = NodeJS.Timer;
|
||||||
|
import { i18n } from '../common/i18n';
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
show: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SNACKBAR_NAMESPACE = 'SnackBar';
|
||||||
|
|
||||||
|
export default class SnackBar extends React.Component<{}, IState> {
|
||||||
|
private snackBarTimer: Timer | undefined;
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
show: false,
|
||||||
|
};
|
||||||
|
this.showSnackBar = this.showSnackBar.bind(this);
|
||||||
|
this.removeSnackBar = this.removeSnackBar.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the custom title bar
|
||||||
|
*/
|
||||||
|
public render(): JSX.Element | undefined {
|
||||||
|
const { show } = this.state;
|
||||||
|
|
||||||
|
return show ? (
|
||||||
|
<div className='SnackBar SnackBar-show'>
|
||||||
|
<span >{i18n.t('Press ', SNACKBAR_NAMESPACE)()}</span>
|
||||||
|
<span className='SnackBar-esc'>{i18n.t('esc', SNACKBAR_NAMESPACE)()}</span>
|
||||||
|
<span >{i18n.t(' to exit full screen', SNACKBAR_NAMESPACE)()}</span>
|
||||||
|
</div>
|
||||||
|
) : <div> </div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentDidMount(): void {
|
||||||
|
ipcRenderer.on('window-enter-full-screen', this.showSnackBar);
|
||||||
|
ipcRenderer.on('window-leave-full-screen', this.removeSnackBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
public componentWillMount(): void {
|
||||||
|
ipcRenderer.removeListener('window-enter-full-screen', this.showSnackBar);
|
||||||
|
ipcRenderer.removeListener('window-leave-full-screen', this.removeSnackBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays snackbar for 3sec
|
||||||
|
*/
|
||||||
|
public showSnackBar(): void {
|
||||||
|
this.setState({ show: true });
|
||||||
|
this.snackBarTimer = setTimeout(() => {
|
||||||
|
this.removeSnackBar();
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes snackbar
|
||||||
|
*/
|
||||||
|
public removeSnackBar(): void {
|
||||||
|
this.setState({ show: false });
|
||||||
|
if (this.snackBarTimer) {
|
||||||
|
clearTimeout(this.snackBarTimer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@ import {
|
|||||||
IBadgeCount,
|
IBadgeCount,
|
||||||
IScreenSnippet,
|
IScreenSnippet,
|
||||||
} from '../common/api-interface';
|
} from '../common/api-interface';
|
||||||
|
import { i18n, LocaleType } from '../common/i18n';
|
||||||
import { throttle } from '../common/utils';
|
import { throttle } from '../common/utils';
|
||||||
|
|
||||||
interface ILocalObject {
|
interface ILocalObject {
|
||||||
@ -90,6 +91,7 @@ export class SSFApi {
|
|||||||
*/
|
*/
|
||||||
public setLocale(locale): void {
|
public setLocale(locale): void {
|
||||||
if (typeof locale === 'string') {
|
if (typeof locale === 'string') {
|
||||||
|
i18n.setLocale(locale as LocaleType);
|
||||||
throttledSetLocale(locale);
|
throttledSetLocale(locale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
52
src/renderer/styles/snack-bar.less
Normal file
52
src/renderer/styles/snack-bar.less
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
@white: rgba(255,255,255, 1);
|
||||||
|
@grey: rgba(51,51,51, 1);
|
||||||
|
|
||||||
|
@-webkit-keyframes fadein {
|
||||||
|
from {
|
||||||
|
top: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
top: 30px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@-webkit-keyframes fadeout {
|
||||||
|
from {
|
||||||
|
top: 30px;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
top: 0;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.snackbar {
|
||||||
|
visibility: hidden;
|
||||||
|
min-width: 250px;
|
||||||
|
margin-left: -135px;
|
||||||
|
background-color: @grey;
|
||||||
|
color: @white;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 16px;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 2147483647;
|
||||||
|
left: 50%;
|
||||||
|
top: 30px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: normal;
|
||||||
|
|
||||||
|
&-esc {
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 2px solid @white;
|
||||||
|
background-color: @grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-show {
|
||||||
|
visibility: visible;
|
||||||
|
-webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
|
||||||
|
animation: fadein 0.5s, fadeout 0.5s 2.5s;
|
||||||
|
}
|
||||||
|
}
|
@ -109,7 +109,7 @@ export default class WindowsTitleBar extends React.Component<{}, IState> {
|
|||||||
>
|
>
|
||||||
<div className='title-bar-button-container'>
|
<div className='title-bar-button-container'>
|
||||||
<button
|
<button
|
||||||
title={i18n.t('Menu')}
|
title={i18n.t('Menu')()}
|
||||||
className='hamburger-menu-button'
|
className='hamburger-menu-button'
|
||||||
onClick={this.eventHandlers.onShowMenu}
|
onClick={this.eventHandlers.onShowMenu}
|
||||||
onMouseDown={this.handleMouseDown}
|
onMouseDown={this.handleMouseDown}
|
||||||
@ -128,7 +128,7 @@ export default class WindowsTitleBar extends React.Component<{}, IState> {
|
|||||||
<div className='title-bar-button-container'>
|
<div className='title-bar-button-container'>
|
||||||
<button
|
<button
|
||||||
className='title-bar-button'
|
className='title-bar-button'
|
||||||
title={i18n.t('Minimize')}
|
title={i18n.t('Minimize')()}
|
||||||
onClick={this.eventHandlers.onMinimize}
|
onClick={this.eventHandlers.onMinimize}
|
||||||
onMouseDown={this.handleMouseDown}
|
onMouseDown={this.handleMouseDown}
|
||||||
>
|
>
|
||||||
@ -143,7 +143,7 @@ export default class WindowsTitleBar extends React.Component<{}, IState> {
|
|||||||
<div className='title-bar-button-container'>
|
<div className='title-bar-button-container'>
|
||||||
<button
|
<button
|
||||||
className='title-bar-button'
|
className='title-bar-button'
|
||||||
title={i18n.t('Close')}
|
title={i18n.t('Close')()}
|
||||||
onClick={this.eventHandlers.onClose}
|
onClick={this.eventHandlers.onClose}
|
||||||
onMouseDown={this.handleMouseDown}
|
onMouseDown={this.handleMouseDown}
|
||||||
>
|
>
|
||||||
@ -169,7 +169,7 @@ export default class WindowsTitleBar extends React.Component<{}, IState> {
|
|||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className='title-bar-button'
|
className='title-bar-button'
|
||||||
title={i18n.t('Maximize')}
|
title={i18n.t('Maximize')()}
|
||||||
onClick={this.eventHandlers.onUnmaximize}
|
onClick={this.eventHandlers.onUnmaximize}
|
||||||
onMouseDown={this.handleMouseDown}
|
onMouseDown={this.handleMouseDown}
|
||||||
>
|
>
|
||||||
@ -185,7 +185,7 @@ export default class WindowsTitleBar extends React.Component<{}, IState> {
|
|||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
className='title-bar-button'
|
className='title-bar-button'
|
||||||
title={i18n.t('unMaximize')}
|
title={i18n.t('unMaximize')()}
|
||||||
onClick={this.eventHandlers.onMaximize }
|
onClick={this.eventHandlers.onMaximize }
|
||||||
onMouseDown={this.handleMouseDown}
|
onMouseDown={this.handleMouseDown}
|
||||||
>
|
>
|
||||||
@ -223,7 +223,6 @@ export default class WindowsTitleBar extends React.Component<{}, IState> {
|
|||||||
*/
|
*/
|
||||||
public maximize(): void {
|
public maximize(): void {
|
||||||
if (this.isValidWindow()) {
|
if (this.isValidWindow()) {
|
||||||
console.log(this.window.maximize());
|
|
||||||
this.window.maximize();
|
this.window.maximize();
|
||||||
this.setState({ isMaximized: true });
|
this.setState({ isMaximized: true });
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"pretty": true,
|
"pretty": true,
|
||||||
"target": "ES2016",
|
"target": "ES2016",
|
||||||
|
"resolveJsonModule": true,
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
"lib": [
|
"lib": [
|
||||||
"es2016",
|
"es2016",
|
||||||
|
@ -2,6 +2,11 @@
|
|||||||
"extends": [
|
"extends": [
|
||||||
"tslint:recommended"
|
"tslint:recommended"
|
||||||
],
|
],
|
||||||
|
"linterOptions": {
|
||||||
|
"exclude": [
|
||||||
|
"**/*.json"
|
||||||
|
]
|
||||||
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"curly": false,
|
"curly": false,
|
||||||
"eofline": false,
|
"eofline": false,
|
||||||
@ -28,7 +33,7 @@
|
|||||||
"no-var-requires": true,
|
"no-var-requires": true,
|
||||||
"only-arrow-functions": true,
|
"only-arrow-functions": true,
|
||||||
"no-console": [
|
"no-console": [
|
||||||
false,
|
true,
|
||||||
"log",
|
"log",
|
||||||
"error"
|
"error"
|
||||||
],
|
],
|
||||||
|
Loading…
Reference in New Issue
Block a user