Typescript (Fix spellchecker issue and bump electron version) (#626)

* Typescript - Fix spellchecker issues and bump electron version to 5.x

* Typescript - Update unit test cases
This commit is contained in:
Kiran Niranjan 2019-04-03 18:25:27 +05:30 committed by Vishwas Shashidhar
parent e8cb240b92
commit efe15967b5
13 changed files with 144 additions and 100 deletions

View File

@ -91,10 +91,13 @@
<br>
<hr>
<p>Change language:<p>
<button id='set-locale-en-US'>Change language to English - US</button>
<button id='set-locale-ja-JP'>Change language to Japanese</button>
<button id='set-locale-fr-FR'>Change language to French</button>
<br>
<select id="locale-select" name="locale">
<option value="en-US">en-US</option>
<option value="ja-JP">ja-JP</option>
<option value="fr-FR">fr-FR</option>
</select>
<button id="changeLocale">Submit</button>
<br>
<hr>
<p>Badge Count:<p>
<button id='inc-badge'>increment badge count</button>
@ -307,26 +310,66 @@
});
});
var setLocaleToEn = document.getElementById('set-locale-en-US');
setLocaleToEn.addEventListener('click', function () {
ssf.setLocale('en-US');
document.location.reload();
});
var setLocaleToJa = document.getElementById('set-locale-ja-JP');
setLocaleToJa.addEventListener('click', function () {
ssf.setLocale('ja-JP');
document.location.reload();
});
var setLocaleToFr = document.getElementById('set-locale-fr-FR');
setLocaleToFr.addEventListener('click', function () {
ssf.setLocale('fr-FR');
document.location.reload();
/**
* Core post message api handler
*/
const postMessage = (method, data) => {
window.postMessage({ method, data }, '*');
};
window.addEventListener('message', (event) => {
const { data, method } = event.data;
switch (method) {
case 'activity-callback':
activityCallback(data);
break;
case 'media-source-callback':
if (data.error) {
if (data.error.message === 'User Cancelled') {
console.error('user canceled');
} else {
throw new Error(data.error);
}
return;
}
const constraints = {
mandatory: {
chromeMediaSource: 'desktop',
maxWidth: screen.width > 1920 ? 1920 : screen.width,
maxHeight: screen.height > 1080 ? 1080 : screen.height,
chromeMediaSourceId: data.source.id
},
optional: []
};
navigator.mediaDevices.getUserMedia({ video: constraints }).then((stream) => {
handleStream(stream, data.source.display_id);
});
break;
case 'screen-sharing-indicator-callback':
if (!data || typeof data.type !== 'string' || !requestId) {
console.error(`screen-sharing-indicator-callback invalid args ${data}`);
} else {
console.log(`screen sharing will be stopped`);
}
break;
default:
console.log(event.data);
}
});
/**
* Protocol handler
*/
if (window.ssf) {
window.ssf.registerProtocolHandler();
} else {
postMessage(apiCmds.registerProtocolHandler);
}
/**
* Activity Detection
*/
const activityDetection = document.getElementById('activity-detection');
const activityStatus = document.getElementById('activity-status');
activityDetection.addEventListener('click', () => {
@ -408,55 +451,20 @@
};
/**
* Core post message api handler
* Locale api handler
*/
window.addEventListener('message', (event) => {
const { data, method } = event.data;
switch (method) {
case 'activity-callback':
activityCallback(data);
break;
case 'media-source-callback':
if (data.error) {
if (data.error.message === 'User Cancelled') {
console.error('user canceled');
} else {
throw new Error(data.error);
}
return;
}
const constraints = {
mandatory: {
chromeMediaSource: 'desktop',
maxWidth: screen.width > 1920 ? 1920 : screen.width,
maxHeight: screen.height > 1080 ? 1080 : screen.height,
chromeMediaSourceId: data.source.id
},
optional: []
};
navigator.mediaDevices.getUserMedia({ video: constraints }).then((stream) => {
handleStream(stream, data.source.display_id);
});
break;
case 'screen-sharing-indicator-callback':
if (!data || typeof data.type !== 'string' || !requestId) {
console.error(`screen-sharing-indicator-callback invalid args ${data}`);
} else {
console.log(`screen sharing will be stopped`);
}
break;
default:
console.log(event.data);
document.getElementById('changeLocale').addEventListener('click', () => {
const locale = document.getElementById('locale-select').value;
if (window.ssf) {
window.ssf.setLocale(locale);
} else {
postMessage(apiCmds.setLocale, locale);
}
});
const postMessage = (method, data) => {
window.postMessage({ method, data }, '*');
};
/**
* Download Manager api handler
*/
const download = (filename, text) => {
const element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
@ -471,13 +479,13 @@
};
// Start file download.
document.getElementById("download-file1").addEventListener("click", () => {
document.getElementById('download-file1').addEventListener('click', () => {
const filename = "hello.txt";
const text = document.getElementById("text-val").value;
download(filename, text);
}, false);
document.getElementById("download-file2").addEventListener("click", () => {
document.getElementById('download-file2').addEventListener('click', () => {
const filename = "bye.txt";
const text = document.getElementById("text-val").value;
download(filename, text);

View File

@ -81,7 +81,8 @@
"devDependencies": {
"@types/auto-launch": "^5.0.0",
"@types/enzyme": "^3.9.0",
"@types/ffi": "0.2.1",
"@types/ffi-napi": "2.4.1",
"@types/ref-napi": "1.4.0",
"@types/jest": "23.3.12",
"@types/lodash.omit": "^4.5.4",
"@types/node": "10.11.4",
@ -98,7 +99,7 @@
"chromedriver": "2.45.0",
"cross-env": "5.2.0",
"del": "3.0.0",
"electron": "4.0.8",
"electron": "5.0.0-beta.7",
"electron-builder": "20.38.4",
"electron-builder-squirrel-windows": "20.38.3",
"electron-chromedriver": "4.0.0-beta.1",
@ -129,7 +130,6 @@
"wdio-selenium-standalone-service": "0.0.12"
},
"dependencies": {
"@types/ffi": "0.2.1",
"archiver": "3.0.0",
"async.map": "0.5.2",
"async.mapseries": "0.5.2",
@ -138,9 +138,9 @@
"electron-dl": "1.12.0",
"electron-fetch": "1.3.0",
"electron-log": "2.2.17",
"electron-spellchecker": "git+https://github.com/symphonyoss/electron-spellchecker.git#v1.1.5",
"electron-spellchecker": "git+https://github.com/symphonyoss/electron-spellchecker.git#v2.0.0",
"enzyme-to-json": "^3.3.5",
"ffi": "git+https://github.com/symphonyoss/node-ffi.git#v1.2.9",
"ffi-napi": "2.4.5",
"filesize": "3.6.1",
"jimp": "0.6.0",
"keymirror": "0.1.1",
@ -151,7 +151,7 @@
"node-osascript": "2.1.0",
"react": "16.6.3",
"react-dom": "^16.6.0",
"ref": "1.3.5",
"ref-napi": "1.4.1",
"shell-path": "2.1.0",
"winreg": "1.2.4"
},

View File

@ -69,7 +69,11 @@ jest.mock('../src/common/logger', () => {
jest.mock('../src/app/config-handler', () => {
return {
config: {
getConfigFields: jest.fn(() => true),
getConfigFields: jest.fn(() => {
return {
bringToFront: true,
};
}),
},
};
});

View File

@ -27,7 +27,7 @@ describe('protocol handler', () => {
});
it('protocol uri should be null by default', () => {
expect(protocolHandlerInstance.protocolUri).toBeNull();
expect(protocolHandlerInstance.protocolUri).toBe('symphony://?userId=22222');
});
it('protocol action should be called when uri is correct', () => {

View File

@ -154,15 +154,15 @@ export class AppMenu {
label: i18n.t('Edit')(),
submenu:
[
this.assignRoleOrLabel('undo', i18n.t('Undo')()),
this.assignRoleOrLabel('redo', i18n.t('Redo')()),
this.assignRoleOrLabel({ role: 'undo', label: i18n.t('Undo')() }),
this.assignRoleOrLabel({ role: 'redo', label: i18n.t('Redo')() }),
this.buildSeparator(),
this.assignRoleOrLabel('cut', i18n.t('Cut')()),
this.assignRoleOrLabel('copy', i18n.t('Copy')()),
this.assignRoleOrLabel('paste', i18n.t('Paste')()),
this.assignRoleOrLabel('pasteandmatchstyle', i18n.t('Paste and Match Style')()),
this.assignRoleOrLabel('delete', i18n.t('Delete')()),
this.assignRoleOrLabel('selectall', i18n.t('Select All')()),
this.assignRoleOrLabel({ role: 'cut', label: i18n.t('Cut')() }),
this.assignRoleOrLabel({ role: 'copy', label: i18n.t('Copy')() }),
this.assignRoleOrLabel({ role: 'paste', label: i18n.t('Paste')() }),
this.assignRoleOrLabel({ role: 'pasteandmatchstyle', label: i18n.t('Paste and Match Style')() }),
this.assignRoleOrLabel({ role: 'delete', label: i18n.t('Delete')() }),
this.assignRoleOrLabel({ role: 'selectall', label: i18n.t('Select All')() }),
],
};
@ -190,11 +190,11 @@ export class AppMenu {
label: i18n.t('Reload')(),
},
this.buildSeparator(),
this.assignRoleOrLabel('resetzoom', i18n.t('Actual Size')()),
this.assignRoleOrLabel('zoomin', i18n.t('Zoom In')()),
this.assignRoleOrLabel('zoomout', i18n.t('Zoom Out')()),
this.assignRoleOrLabel({ role: 'resetzoom', label: i18n.t('Actual Size')() }),
this.assignRoleOrLabel({ role: 'zoomin', label: i18n.t('Zoom In')() }),
this.assignRoleOrLabel({ role: 'zoomout', label: i18n.t('Zoom Out')() }),
this.buildSeparator(),
this.assignRoleOrLabel('togglefullscreen', i18n.t('Toggle Full Screen')()),
this.assignRoleOrLabel({ role: 'togglefullscreen', label: i18n.t('Toggle Full Screen')() }),
],
};
}
@ -204,8 +204,8 @@ export class AppMenu {
*/
private buildWindowMenu(): Electron.MenuItemConstructorOptions {
const submenu: MenuItemConstructorOptions[] = [
this.assignRoleOrLabel('minimize', i18n.t('Minimize')()),
this.assignRoleOrLabel('close', i18n.t('Close')()),
this.assignRoleOrLabel({ role: 'minimize', label: i18n.t('Minimize')() }),
this.assignRoleOrLabel({ role: 'close', label: i18n.t('Close')() }),
this.buildSeparator(),
{
checked: launchOnStartup,
@ -348,14 +348,14 @@ export class AppMenu {
* @return {Object}.role The action of the menu item
* @return {Object}.accelerator keyboard shortcuts and modifiers
*/
private assignRoleOrLabel(role: string, label: string) {
private assignRoleOrLabel({ role, label }: MenuItemConstructorOptions): MenuItemConstructorOptions {
if (isMac) {
return label ? { role, label } : { role };
}
if (isWindowsOS) {
return label ? { role, label, accelerator: windowsAccelerator[ role ] || '' }
: { role, accelerator: windowsAccelerator[ role ] || '' };
return label ? { role, label, accelerator: role ? windowsAccelerator[ role ] : '' }
: { role, accelerator: role ? windowsAccelerator[ role ] : '' };
}
return label ? { role, label } : { role };

View File

@ -1,7 +1,7 @@
import { app } from 'electron';
import { Library } from 'ffi';
import { Library } from 'ffi-napi';
import * as path from 'path';
import { refType, types } from 'ref';
import { refType, types } from 'ref-napi';
const execPath = path.dirname(app.getPath('exe'));
import { isDevEnv, isMac } from '../common/env';

View File

@ -127,6 +127,11 @@ ipcMain.on(apiName.symphonyApi, (event: Electron.Event, arg: IApiArgs) => {
downloadManagerAction(arg.type, arg.path);
}
break;
case apiCmds.isMisspelled:
if (typeof arg.word === 'string') {
event.returnValue = windowHandler.spellchecker ? windowHandler.spellchecker.isMisspelled(arg.word) : false;
}
break;
default:
}

View File

@ -11,7 +11,6 @@ import { config } from './config-handler';
import './dialog-handler';
import './main-api-handler';
import { protocolHandler } from './protocol-handler';
import { SpellChecker } from './spell-check-handler';
import { ICustomBrowserWindow, windowHandler } from './window-handler';
const allowMultiInstance: string | boolean = getCommandLineArgs(process.argv, '--multiInstance', true) || isDevEnv;
@ -29,8 +28,6 @@ if (isMac) {
*/
const startApplication = async () => {
await app.whenReady();
const spellchecker = new SpellChecker();
logger.info(`initialized spellchecker module with locale ${spellchecker.locale}`);
createAppCacheFile();
windowHandler.showLoadingScreen();
windowHandler.createApplication();

View File

@ -1,5 +1,5 @@
import { apiName } from '../common/api-interface';
import { isMac, isWindowsOS } from '../common/env';
import { isMac } from '../common/env';
import { getCommandLineArgs } from '../common/utils';
import { activate } from './window-actions';
@ -61,7 +61,7 @@ class ProtocolHandler {
*/
public processArgv(argv?: string[]): void {
const protocolUriFromArgv = getCommandLineArgs(argv || process.argv, protocol.SymphonyProtocol, false);
if (isWindowsOS && protocolUriFromArgv) {
if (protocolUriFromArgv) {
this.sendProtocol(protocolUriFromArgv, false);
}
}

View File

@ -32,7 +32,6 @@ export class SpellChecker {
const contextMenuListener = (_event, info) => {
if (this.locale !== i18n.getLocale()) {
contextMenuBuilder.setAlternateStringFormatter(this.getStringTable());
this.spellCheckHandler.updateContextMenuLocale(i18n.t('ContextMenu')());
this.locale = i18n.getLocale();
}
contextMenuBuilder.showPopupMenu(info);
@ -44,6 +43,17 @@ export class SpellChecker {
});
}
/**
* Predicts if the given text is misspelled
* @param text
*/
public isMisspelled(text: string): boolean {
if (!this.spellCheckHandler) {
return false;
}
return this.spellCheckHandler.isMisspelled(text);
}
/**
* Builds the string table for context menu
*

View File

@ -14,6 +14,7 @@ import { notification } from '../renderer/notification';
import { AppMenu } from './app-menu';
import { handleChildWindow } from './child-window-handler';
import { config, IConfig } from './config-handler';
import { SpellChecker } from './spell-check-handler';
import { monitorWindowActions } from './window-actions';
import {
createComponentWindow,
@ -175,6 +176,7 @@ export class WindowHandler {
public isOnline: boolean;
public url: string | undefined;
public willQuitApp: boolean = false;
public spellchecker: SpellChecker | undefined;
private readonly windowOpts: ICustomBrowserWindowConstructorOpts;
private readonly globalConfig: IConfig;
@ -218,6 +220,10 @@ export class WindowHandler {
* Starting point of the app
*/
public createApplication() {
this.spellchecker = new SpellChecker();
logger.info(`initialized spellchecker module with locale ${this.spellchecker.locale}`);
// set window opts with additional config
this.mainWindow = new BrowserWindow({
...this.windowOpts, ...getBounds(this.config.mainWinPos, DEFAULT_WIDTH, DEFAULT_HEIGHT),

View File

@ -27,6 +27,7 @@ export enum apiCmds {
notification = 'notification',
closeNotification = 'close-notification',
initMainWindow = 'init-main-window',
isMisspelled = 'is-misspelled',
}
export enum apiName {
@ -35,6 +36,7 @@ export enum apiName {
}
export interface IApiArgs {
word: string;
cmd: apiCmds;
isOnline: boolean;
count: number;

View File

@ -1,4 +1,4 @@
import { ipcRenderer } from 'electron';
import { ipcRenderer, webFrame } from 'electron';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { apiCmds, apiName } from '../common/api-interface';
@ -58,6 +58,18 @@ ipcRenderer.on('page-load', (_event, { locale, resources, origin, enableCustomTi
ReactDOM.render(element, div);
}
webFrame.setSpellCheckProvider('en-US', {
spellCheck(words, callback) {
const misspelled = words.filter((word) => {
return ipcRenderer.sendSync(apiName.symphonyApi, {
cmd: apiCmds.isMisspelled,
word,
});
});
callback(misspelled);
},
});
// injects snack bar
const snackBar = React.createElement(SnackBar);
const snackBarContainer = document.createElement( 'div' );