Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Electron spellchecker #33

Merged
merged 10 commits into from
Dec 14, 2018
Merged
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,19 @@ We are defaulting in opening the links in a external browser, but links can be o

Switching the userAgent with the persistence turn on sometimes have the side effect of "loosing" the channels history. Removing the data under `~/.config/teams-for-linux` should fix the issue.

<<<<<<< HEAD
### No desktop notifications

Some notifications daemons in linux don't support the implementation that Microsoft implemented in the browser.

This project includes a desktop notification hack that can be enable by running the application with `teams --enableDesktopNotificationsHack`.

Read more about this and another config arguments in the [config README.md](config/README.md) file.
=======
### Double notifications

Some notifications daemons in linux can end up generating double notifications (like in the cast of Dunst). If this happen you can run the application with `teams --disableDesktopNotificationsHack` that will disable the notifications implemented in this client.
>>>>>>> master

## License

Expand Down
4 changes: 4 additions & 0 deletions app/browser/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
# Browser

The files in here handle the code that talks with the browser page.

The [index.js](index.js) is the entry point it load the [zoom.js](zoom.js) and the [pageTitleNotifications.js](pageTitleNotifications.js) files.

The [zoom.js](zoom.js) inject the keyboard shortcuts for zoom in the browser.

The [spellchecker.js](spellchecker.js) handles the spellchecker and right menu click funcionality. We are leveraging the spellchecker capabilitites to [electron-spell-check-provider](https://www.npmjs.com/package/electron-spell-check-provider) and the right click menu to the [electron-editor-context-menu](https://github.com/mixmaxhq/electron-editor-context-menu) modules.

The [pageTitleNotifications.js](pageTitleNotifications.js) file handles the emitting of an event when the page-title changes and indicates that there is an unread message.

It also generates a new icon with the notifications count. This can be use later by the tray or app to modify the app icon.
1 change: 1 addition & 0 deletions app/browser/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
const path = require('path');
const { ipcRenderer, remote } = require('electron');
const pageTitleNotifications = require('./pageTitleNotifications');
require('./spellchecker');
require('./zoom')();

const iconPath = path.join(__dirname, '../assets/icons/icon-96x96.png');
Expand Down
40 changes: 40 additions & 0 deletions app/browser/spellchecker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* This code comes from the electron-spell-check-provider README.md. Refer to it for further details.
*
* I have remove most of the comments and extend it to detect the app locale.
*/
const { remote, webFrame } = require('electron');
const SpellCheckProvider = require('electron-spell-check-provider');
const buildEditorContextMenu = remote.require('electron-editor-context-menu');
const appLocale = remote.app.getLocale();
let selection;

function resetSelection() {
selection = {
isMisspelled: false,
spellingSuggestions: []
};
}

function updateSelectionWithSuggestions(suggestions) {
if (window.getSelection().toString()) {
selection.isMisspelled = true;
selection.spellingSuggestions = suggestions.slice(0, 5);
}
}

spellCheckProviderCallback = new SpellCheckProvider(appLocale).on('misspelling', updateSelectionWithSuggestions);

window.addEventListener('mousedown', resetSelection);

webFrame.setSpellCheckProvider(appLocale, true, spellCheckProviderCallback);

window.addEventListener('contextmenu', (e) => {
if (!e.target.closest('textarea, input, [contenteditable="true"]')) return;
setTimeout(() => {
var menu = buildEditorContextMenu(selection);
menu.popup(remote.getCurrentWindow());
}, 30);
});

resetSelection();
4 changes: 2 additions & 2 deletions app/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ function argv(configPath) {
.env(true)
.config(path.join(configPath, 'teams.json'))
.options({
disableDesktopNotificationsHack: {
enableDesktopNotificationsHack: {
default: false,
describe: 'Disable electron-native-notifications hack',
describe: 'Enable electron-native-notifications hack',
type: 'boolean',
},
url: {
Expand Down
106 changes: 45 additions & 61 deletions app/index.js
Original file line number Diff line number Diff line change
@@ -1,82 +1,28 @@
const { shell, app, ipcMain, BrowserWindow } = require('electron');
const { shell, app, BrowserWindow } = require('electron');
const windowStateKeeper = require('electron-window-state');
const NativeNotification = require('electron-native-notification');
const path = require('path');
const iconPath = path.join(__dirname, 'assets', 'icons', 'icon-96x96.png');
const config = require('./config')(app.getPath('userData'));
const login = require('./login');
const Menus = require('./menus');

const notifications = require('./notifications');

global.edgeUserAgent = config.edgeUserAgent;

function createWindow() {
// Load the previous state with fallback to defaults
const windowState = windowStateKeeper({
defaultWidth: 0,
defaultHeight: 0,
});

// Create the window
const window = new BrowserWindow({
x: windowState.x,
y: windowState.y,

width: windowState.width,
height: windowState.height,

show: false,
autoHideMenuBar: true,
icon: iconPath,

webPreferences: {
partition: config.partition,
preload: path.join(__dirname, 'browser', 'index.js'),
nativeWindowOpen: true,
plugins: true,
nodeIntegration: false,
},
});

windowState.manage(window);
window.eval = global.eval = function () { // eslint-disable-line no-eval
throw new Error('Sorry, this app does not support window.eval().');
};

return window;
}

app.commandLine.appendSwitch('auth-server-whitelist', config.authServerWhitelist);
app.commandLine.appendSwitch('enable-ntlm-v2', config.ntlmV2enabled);

app.on('ready', () => {
let isFirstLoginTry = true;
let isFirstLoginTry = false;
let window = createWindow();
new Menus(window, config, iconPath);

window.on('page-title-updated', (event, title) => {
window.webContents.send('page-title', title);
});

if (!config.disableDesktopNotificationsHack) {
ipcMain.on('notifications', async (e, msg) => {
if (msg.count > 0) {
const body = ((msg.text) ? `(${msg.count}): ${msg.text}` : `You got ${msg.count} notification(s)`);
const notification = new NativeNotification(
'Microsoft Teams',
{
body,
icon: iconPath,
},
);
notification.onclick = () => {
window.show();
window.focus();
};
if (notification.show !== undefined) {
notification.show();
}
}
});
if (config.enableDesktopNotificationsHack) {
notifications.addDesktopNotificationHack(iconPath);
}

window.webContents.on('new-window', (event, url, frame, disposition) => {
Expand All @@ -86,12 +32,14 @@ app.on('ready', () => {
}
});

//login.handleLoginDialogTry(window.webContents);
window.webContents.on('login', (event, request, authInfo, callback) => {
event.preventDefault();
if (isFirstLoginTry) {
isFirstLoginTry = false;
login.loginService(window, callback);
} else {
// if fails to authenticate we need to relanch the app as we are closed
isFirstLoginTry = true;
app.relaunch();
app.exit(0);
Expand All @@ -114,4 +62,40 @@ app.on('ready', () => {
if (config.webDebug) {
window.openDevTools();
}
});
});

function createWindow() {
// Load the previous state with fallback to defaults
const windowState = windowStateKeeper({
defaultWidth: 0,
defaultHeight: 0,
});

// Create the window
const window = new BrowserWindow({
x: windowState.x,
y: windowState.y,

width: windowState.width,
height: windowState.height,

show: false,
autoHideMenuBar: true,
icon: iconPath,

webPreferences: {
partition: config.partition,
preload: path.join(__dirname, 'browser', 'index.js'),
nativeWindowOpen: true,
plugins: true,
nodeIntegration: false,
},
});

windowState.manage(window);
window.eval = global.eval = function () { // eslint-disable-line no-eval
throw new Error('Sorry, this app does not support window.eval().');
};

return window;
}
18 changes: 17 additions & 1 deletion app/login/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
const {ipcMain, BrowserWindow} = require('electron');
// const {app, ipcMain, BrowserWindow} = require('electron');
// let isFirstLoginTry = true;

// exports.handleLoginDialogTry= function handleLoginDialogTry(webContents) {
// webContents.on('login', (event, request, authInfo, callback) => {
// event.preventDefault();
// if (isFirstLoginTry) {
// isFirstLoginTry = false;
// this.loginService(window, callback);
// } else {
// // if fails to authenticate we need to relanch the app as we are closed
// isFirstLoginTry = true;
// app.relaunch();
// app.exit(0);
// }
// });
// };

exports.loginService = function loginService(parentWindow, callback) {
let win = new BrowserWindow({
Expand Down
12 changes: 8 additions & 4 deletions app/menus/help.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
const open = require('opn');
const {shell} = require('electron');

exports = module.exports = (app) => ({
label: 'Help',
submenu: [
{
label: 'Online Documentation',
click: () => open('https://support.office.com/en-us/teams?omkt=en-001'),
click: () => shell.openExternal('https://support.office.com/en-us/teams?omkt=en-001'),
},
{
label: 'Github Project',
click: () => open('https://github.com/IsmaelMartinez/teams-for-linux'),
click: () => shell.openExternal('https://github.com/IsmaelMartinez/teams-for-linux'),
},
{type: 'separator'},
{
label: `Version ${app.getVersion()}`,
label: `Teams-for-linux version ${require('./../../package.json').version}`,
enabled: false,
},
{
label: `Electron version ${app.getVersion()}`,
enabled: false,
},
{role: 'toggledevtools'},
Expand Down
2 changes: 1 addition & 1 deletion app/menus/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class Menus {
this.window.setMenu(Menu.buildFromTemplate([
appMenu,
preferences(),
help(app),
help(app, this.window),
]));

this.window.on('close', (event) => {
Expand Down
7 changes: 7 additions & 0 deletions app/notifications/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
This folder has the code that handles the desktop notifications hack implementation.

There only the [index.js](index.js) file. The desktop notification is a temporary solution that will be hopefully face out as linux clients start implementing its support.

At the time of writing, Ubuntu 18 does support the notications so it has been deem necessary to disable this hack.

It is possible to enable it by using the configuration options. Check the [config README.md](../config/README.md) for more info on how to enableDesktopNotificationsHack
24 changes: 24 additions & 0 deletions app/notifications/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const {ipcMain} = require('electron');
const NativeNotification = require('electron-native-notification');

exports.addDesktopNotificationHack = function addDesktopNotificationHack(iconPath) {
ipcMain.on('notifications', async (e, msg) => {
if (msg.count > 0) {
const body = ((msg.text) ? `(${msg.count}): ${msg.text}` : `You got ${msg.count} notification(s)`);
const notification = new NativeNotification(
'Microsoft Teams',
{
body,
icon: iconPath,
},
);
notification.onclick = () => {
window.show();
window.focus();
};
if (notification.show !== undefined) {
notification.show();
}
}
});
};
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@
"dependencies": {
"electron-native-notification": "^1.2.1",
"electron-window-state": "^4.1.1",
"opn": "^5.3.0",
"yargs": "^12.0.1"
"yargs": "^12.0.1",
"electron-spell-check-provider": "*",
"electron-editor-context-menu": "*"
},
"devDependencies": {
"electron": "^3.0.0",
Expand Down
Loading