Compare commits

..

19 Commits

Author SHA1 Message Date
akashnimare
292e1b4d15 test: set default language for spellchecker 2017-12-15 19:21:07 +05:30
akashnimare
64a12dca09 test: run all the tests on appveyor. 2017-12-14 00:26:07 +05:30
akashnimare
d1d19327ba travis: rename tests. 2017-12-14 00:03:46 +05:30
Akash Nimare
dc6a1c3c0b travis: only run e2e & linting test on Linux 2017-12-13 23:46:57 +05:30
simplyahmazing
63005d20ca combine test cmds into one cmd 2017-12-13 10:10:02 -05:00
simplyahmazing
600e8acdfa Merge branch 'master' into setup-karma-for-unit-tests 2017-12-13 09:54:05 -05:00
simplyahmazing
3f01774d0a fix app starts e2e test 2017-12-11 16:42:09 -05:00
simplyahmazing
808dffd2ed merge upstream master 2017-12-11 16:37:32 -05:00
simplyahmazing
6b51f6d9a6 enable spellchecker via config for test 2017-12-11 16:34:23 -05:00
simplyahmazing
36342145da remove flag to force enable spellchecker 2017-12-11 16:20:52 -05:00
simplyahmazing
7b639129b3 merge upstream master 2017-12-06 11:53:50 -05:00
simplyahmazing
89ae4585e6 rename test file 2017-12-06 11:45:02 -05:00
simplyahmazing
469b827425 remove unused dep section from package.json 2017-12-06 11:43:52 -05:00
simplyahmazing
7feb0e4280 remove karma-coverage 2017-12-06 11:33:58 -05:00
simplyahmazing
8cd8bac6a7 remove browserify dep 2017-12-06 06:51:30 -05:00
simplyahmazing
ce1e902e89 run unit tests on travis/appveyor 2017-12-05 13:34:25 -05:00
simplyahmazing
3baa2c6ca5 setup spellchecker test 2017-12-05 13:27:07 -05:00
simplyahmazing
2114ccfb40 setup karma for running renderer process tests 2017-12-05 13:25:51 -05:00
simplyahmazing
c6f1f99fe9 move integration tests into e2e dir 2017-11-30 20:47:25 -05:00
53 changed files with 978 additions and 4614 deletions

View File

@@ -1,8 +0,0 @@
---
<!-- Please Include: -->
- **Operating System**:
- [ ] Windows
- [ ] Linux/Ubutnu
- [ ] macOS
- **Clear steps to reproduce the issue**:
- **Relevant error messages and/or screenshots**:

View File

@@ -1,16 +0,0 @@
---
<!--
Remove the fields that are not appropriate
Please include:
-->
**What's this PR do?**
**Any background context you want to provide?**
**Screenshots?**
**You have tested this PR on:**
- [ ] Windows
- [ ] Linux/Ubuntu
- [ ] macOS

3
.gitignore vendored
View File

@@ -26,6 +26,7 @@ yarn-error.log*
config.gypi
# Test generated files
tests/package.json
tests/e2e/package.json
coverage
.python-version

7
ISSUE_TEMPLATE.md Normal file
View File

@@ -0,0 +1,7 @@
---
Please include:
- `Operating System`
- `Clear steps to reproduce the issue`
- `Relevant error messages and/or screenshots`

View File

@@ -12,12 +12,19 @@ Please see [installation guide](https://zulipchat.com/help/desktop-app-install-g
# Features
* Sign in to multiple teams
* Desktop Notifications with inline reply support
* Multilanguage SpellChecker
* Native desktop Notifications
* SpellChecker
* OSX/Win/Linux installers
* Automatic Updates (macOS/Windows/Linux)
* Automatic Updates (macOS/Windows)
* Keyboard shortcuts
Description | Keys
-----------------------| -----------------------
Default shortcuts | <kbd>Cmd/Ctrl</kbd> <kbd>k</kbd>
Manage Zulip Servers | <kbd>Cmd/Ctrl</kbd> <kbd>,</kbd>
Back | <kbd>Cmd/Ctrl</kbd> <kbd>[</kbd>
Forward | <kbd>Cmd/Ctrl</kbd> <kbd>]</kbd>
# Development
Please see our [development guide](./development.md) to get started and run app locally.

View File

@@ -1,4 +1,5 @@
'use strict';
const fs = require('fs');
const { app, dialog } = require('electron');
const { autoUpdater } = require('electron-updater');
const isDev = require('electron-is-dev');
@@ -11,15 +12,13 @@ function appUpdater() {
return;
}
if (process.platform === 'linux' && !process.env.APPIMAGE) {
const { linuxUpdateNotification } = require('./linuxupdater');
linuxUpdateNotification();
return;
}
// Create Logs directory
const LogsDir = `${app.getPath('userData')}/Logs`;
if (!fs.existsSync(LogsDir)) {
fs.mkdirSync(LogsDir);
}
// Log whats happening
const log = require('electron-log');

View File

@@ -12,7 +12,6 @@ const { setAutoLaunch } = require('./startup');
const { app, ipcMain } = electron;
const BadgeSettings = require('./../renderer/js/pages/preference/badge-settings.js');
const ConfigUtil = require('./../renderer/js/utils/config-util.js');
// Adds debug features like hotkeys for triggering dev tools and reload
// in development mode
@@ -82,11 +81,7 @@ function createMainWindow() {
});
win.once('ready-to-show', () => {
if (ConfigUtil.getConfigItem('startMinimized')) {
win.minimize();
} else {
win.show();
}
win.show();
});
win.loadURL(mainURL);
@@ -129,9 +124,6 @@ function createMainWindow() {
return win;
}
// Decrease load on GPU (experimental)
app.disableHardwareAcceleration();
// eslint-disable-next-line max-params
app.on('certificate-error', (event, webContents, url, error, certificate, callback) => {
event.preventDefault();
@@ -153,11 +145,7 @@ app.on('ready', () => {
const page = mainWindow.webContents;
page.on('dom-ready', () => {
if (ConfigUtil.getConfigItem('startMinimized')) {
mainWindow.minimize();
} else {
mainWindow.show();
}
mainWindow.show();
});
page.once('did-frame-finish-load', () => {
@@ -166,11 +154,9 @@ app.on('ready', () => {
crashHandler();
});
// Temporarily remove this event
// electron.powerMonitor.on('resume', () => {
// mainWindow.reload();
// page.send('destroytray');
// });
electron.powerMonitor.on('resume', () => {
page.send('reload-viewer');
});
ipcMain.on('focus-app', () => {
mainWindow.show();
@@ -222,6 +208,11 @@ app.on('ready', () => {
appMenu.setMenu(props);
});
ipcMain.on('register-server-tab-shortcut', (event, index) => {
// Array index == Shown index - 1
page.send('switch-server-tab', index - 1);
});
ipcMain.on('toggleAutoLauncher', (event, AutoLaunchValue) => {
setAutoLaunch(AutoLaunchValue);
});

View File

@@ -1,42 +0,0 @@
const { app } = require('electron');
const { Notification } = require('electron');
const request = require('request');
const semver = require('semver');
const ConfigUtil = require('../renderer/js/utils/config-util');
const LinuxUpdateUtil = require('../renderer/js/utils/linux-update-util');
function linuxUpdateNotification() {
let url = 'https://api.github.com/repos/zulip/zulip-electron/releases';
url = ConfigUtil.getConfigItem('betaUpdate') ? url : url + '/latest';
const options = {
url,
headers: {'User-Agent': 'request'}
};
request(options, (error, response, body) => {
if (error) {
console.log('Error:', error);
return;
}
if (response.statusCode < 400) {
const data = JSON.parse(body);
const latestVersion = ConfigUtil.getConfigItem('betaUpdate') ? data[0].tag_name : data.tag_name;
if (semver.gt(latestVersion, app.getVersion())) {
const notified = LinuxUpdateUtil.getUpdateItem(latestVersion);
if (notified === null) {
new Notification({title: 'Zulip Update', body: 'A new version ' + latestVersion + ' is available. Please update using your package manager.'}).show();
LinuxUpdateUtil.setUpdateItem(latestVersion, true);
}
}
} else {
console.log('Status:', response.statusCode);
}
});
}
module.exports = {
linuxUpdateNotification
};

View File

@@ -54,7 +54,7 @@ class AppMenu {
role: 'togglefullscreen'
}, {
label: 'Zoom In',
accelerator: process.platform === 'darwin' ? 'Command+Plus' : 'Control+=',
accelerator: 'CommandOrControl+=',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('zoomIn');
@@ -115,33 +115,26 @@ class AppMenu {
}
getHelpSubmenu() {
return [
{
label: `${appName + ' Desktop-'} v${app.getVersion()}`,
enabled: false
},
{
label: `${appName} Help`,
click() {
shell.openExternal('https://zulipchat.com/help/');
}
}, {
label: 'Show App Logs',
click() {
shell.openItem(app.getPath('userData'));
}
}, {
label: 'Report an Issue...',
click() {
const body = `
return [{
label: `${appName} Website`,
click() {
shell.openExternal('https://zulipchat.com/help/');
}
}, {
label: `${appName + 'Desktop'} - ${app.getVersion()}`,
enabled: false
}, {
label: 'Report an Issue...',
click() {
const body = `
<!-- Please succinctly describe your issue and steps to reproduce it. -->
-
${app.getName()} ${app.getVersion()}
Electron ${process.versions.electron}
${process.platform} ${process.arch} ${os.release()}`;
shell.openExternal(`https://github.com/zulip/zulip-electron/issues/new?body=${encodeURIComponent(body)}`);
}
}];
shell.openExternal(`https://github.com/zulip/zulip-electron/issues/new?body=${encodeURIComponent(body)}`);
}
}];
}
getWindowSubmenu(tabs, activeTabIndex) {
@@ -157,11 +150,6 @@ class AppMenu {
type: 'separator'
});
for (let i = 0; i < tabs.length; i++) {
// Do not add functional tab settings to list of windows in menu bar
if (tabs[i].props.role === 'function' && tabs[i].webview.props.name === 'Settings') {
continue;
}
initialSubmenu.push({
label: tabs[i].webview.props.name,
accelerator: tabs[i].props.role === 'function' ? '' : `${ShortcutKey} + ${tabs[i].props.index + 1}`,
@@ -171,7 +159,7 @@ class AppMenu {
AppMenu.sendAction('switch-server-tab', tabs[i].props.index);
}
},
type: 'checkbox'
type: 'radio'
});
}
}
@@ -194,7 +182,7 @@ class AppMenu {
}, {
type: 'separator'
}, {
label: 'Desktop App Settings',
label: 'Settings',
accelerator: 'Cmd+,',
click(item, focusedWindow) {
if (focusedWindow) {
@@ -203,7 +191,7 @@ class AppMenu {
}
}, {
label: 'Keyboard Shortcuts',
accelerator: 'Cmd+Shift+K',
accelerator: 'Cmd+K',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('shortcut');
@@ -294,7 +282,7 @@ class AppMenu {
}, {
type: 'separator'
}, {
label: 'Desktop App Settings',
label: 'Settings',
accelerator: 'Ctrl+,',
click(item, focusedWindow) {
if (focusedWindow) {
@@ -305,7 +293,7 @@ class AppMenu {
type: 'separator'
}, {
label: 'Keyboard Shortcuts',
accelerator: 'Ctrl+Shift+K',
accelerator: 'Ctrl+K',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('shortcut');

592
app/package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "zulip",
"version": "1.8.1",
"version": "1.7.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -23,20 +23,6 @@
"nan": "2.8.0"
}
},
"@sindresorhus/is": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz",
"integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow=="
},
"aggregate-error": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-1.0.0.tgz",
"integrity": "sha1-iINE2tAiCnLjr1CQYRf0h3GSX6w=",
"requires": {
"clean-stack": "1.3.0",
"indent-string": "3.2.0"
}
},
"ajv": {
"version": "4.11.8",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
@@ -115,12 +101,6 @@
"tweetnacl": "0.14.5"
}
},
"bindings": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz",
"integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw==",
"optional": true
},
"bluebird": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
@@ -151,43 +131,32 @@
"concat-map": "0.0.1"
}
},
"cacheable-request": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz",
"integrity": "sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=",
"builder-util-runtime": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-3.2.0.tgz",
"integrity": "sha512-VRvyyLiZZSBjcUTqEsHlBJSK0s6uVQChO7kbmVeU6QmSJ7TtsotNQELO6lbahwZMAQ4Z/haCKhlLBDdhW+3aqA==",
"requires": {
"clone-response": "1.0.2",
"get-stream": "3.0.0",
"http-cache-semantics": "3.8.1",
"keyv": "3.0.0",
"lowercase-keys": "1.0.0",
"normalize-url": "2.0.1",
"responselike": "1.0.2"
"bluebird-lst": "1.0.5",
"debug": "3.1.0",
"fs-extra-p": "4.4.4",
"sax": "1.2.4"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
}
}
},
"capture-stack-trace": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz",
"integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0="
},
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
},
"clean-stack": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-1.3.0.tgz",
"integrity": "sha1-noIVAa6XmYbEax1m0tQy2y/UrjE="
},
"clone-response": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz",
"integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=",
"requires": {
"mimic-response": "1.0.0"
}
},
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -211,14 +180,6 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"create-error-class": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
"integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
"requires": {
"capture-stack-trace": "1.0.0"
}
},
"cryptiles": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
@@ -250,19 +211,6 @@
"ms": "2.0.0"
}
},
"decode-uri-component": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
},
"decompress-response": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz",
"integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=",
"requires": {
"mimic-response": "1.0.0"
}
},
"deep-equal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
@@ -273,28 +221,6 @@
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"dns-packet": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz",
"integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==",
"requires": {
"ip": "1.1.5",
"safe-buffer": "5.1.1"
}
},
"dns-socket": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/dns-socket/-/dns-socket-1.6.3.tgz",
"integrity": "sha512-/mUy3VGqIP69dAZjh2xxHXcpK9wk2Len1Dxz8mWAdrIgFC8tnR/aQAyU4a+UTXzOcTvEvGBdp1zFiwnpWKaXng==",
"requires": {
"dns-packet": "1.3.1"
}
},
"duplexer3": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
"integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
},
"ecc-jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
@@ -347,63 +273,19 @@
}
},
"electron-updater": {
"version": "2.18.2",
"resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-2.18.2.tgz",
"integrity": "sha512-Kl7zK2E5R/EBYZapDUxWEHDE/ybD+Gw9gheqIy4uVSeKWjkkQkit/QpPOK8LaQwbXD5vDawp6lrB7Z0Yoacd2A==",
"version": "2.16.2",
"resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-2.16.2.tgz",
"integrity": "sha512-gv1kezjdXR6sw266aTzfs7HgPO5vaf5TsBFh7kMi47JABAIJSO3n+U79pSBperPVtGdqWQ4WfM6+2irrFvYXLw==",
"requires": {
"bluebird-lst": "1.0.5",
"builder-util-runtime": "4.0.5",
"builder-util-runtime": "3.2.0",
"electron-is-dev": "0.3.0",
"fs-extra-p": "4.5.0",
"fs-extra-p": "4.4.4",
"js-yaml": "3.10.0",
"lazy-val": "1.0.3",
"lazy-val": "1.0.2",
"lodash.isequal": "4.5.0",
"semver": "5.4.1"
},
"dependencies": {
"builder-util-runtime": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-4.0.5.tgz",
"integrity": "sha512-NT8AxWH6miZQHnZzaTVjVp1uc6C/mWlxi6GQXKpd4CwyTQd3rT7+poOGrcOhtIiHYCL9VEbRsVfxUAPPsgqJdg==",
"requires": {
"bluebird-lst": "1.0.5",
"debug": "3.1.0",
"fs-extra-p": "4.5.0",
"sax": "1.2.4"
}
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"fs-extra": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz",
"integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==",
"requires": {
"graceful-fs": "4.1.11",
"jsonfile": "4.0.0",
"universalify": "0.1.1"
}
},
"fs-extra-p": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/fs-extra-p/-/fs-extra-p-4.5.0.tgz",
"integrity": "sha512-V/sdZmV+Yx3+nfXmjRTdBP4mVWCt7hZ0+ZOv+IZo+6fdkBxafaGsI7mYeNv/J3rWyz+mIToCFQORFSwt1bZw8Q==",
"requires": {
"bluebird-lst": "1.0.5",
"fs-extra": "5.0.0"
}
},
"lazy-val": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.3.tgz",
"integrity": "sha512-pjCf3BYk+uv3ZcPzEVM0BFvO9Uw58TmlrU0oG5tTrr9Kcid3+kdKxapH8CjdYmVa2nO5wOoZn2rdvZx2PKj/xg=="
}
"semver": "5.4.1",
"source-map-support": "0.5.0"
}
},
"electron-window-state": {
@@ -436,12 +318,6 @@
"resolved": "https://registry.npmjs.org/event-kit/-/event-kit-2.4.0.tgz",
"integrity": "sha512-ZXd9jxUoc/f/zdLdR3OUcCzT84WnpaNWefquLyE125akIC90sDs8S3T/qihliuVsaj7Osc0z8lLL2fjooE9Z4A=="
},
"event-target-shim": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-1.1.1.tgz",
"integrity": "sha1-qG5e5r2qFgVEddp5fM3fDFVphJE=",
"optional": true
},
"extend": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
@@ -467,13 +343,23 @@
"mime-types": "2.1.17"
}
},
"from2": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
"integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
"fs-extra": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.2.tgz",
"integrity": "sha1-+RcExT0bRh+JNFKwwwfZmXZHq2s=",
"requires": {
"inherits": "2.0.3",
"readable-stream": "2.3.4"
"graceful-fs": "4.1.11",
"jsonfile": "4.0.0",
"universalify": "0.1.1"
}
},
"fs-extra-p": {
"version": "4.4.4",
"resolved": "https://registry.npmjs.org/fs-extra-p/-/fs-extra-p-4.4.4.tgz",
"integrity": "sha512-zHsMNJWhXD184QfHKEIFSQSgAFNV7v9J+Nt2XpaLZp2nTz6WxZNV+R4G2uYeGeLTMaKvUZiqGKrH/4iFCupcUA==",
"requires": {
"bluebird-lst": "1.0.5",
"fs-extra": "4.0.2"
}
},
"fs.realpath": {
@@ -481,11 +367,6 @@
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"get-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
},
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
@@ -513,24 +394,6 @@
"path-is-absolute": "1.0.1"
}
},
"got": {
"version": "6.7.1",
"resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
"integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
"requires": {
"create-error-class": "3.0.2",
"duplexer3": "0.1.4",
"get-stream": "3.0.0",
"is-redirect": "1.0.0",
"is-retry-allowed": "1.1.0",
"is-stream": "1.1.0",
"lowercase-keys": "1.0.0",
"safe-buffer": "5.1.1",
"timed-out": "4.0.1",
"unzip-response": "2.0.1",
"url-parse-lax": "1.0.0"
}
},
"graceful-fs": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
@@ -550,19 +413,6 @@
"har-schema": "1.0.5"
}
},
"has-symbol-support-x": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.1.tgz",
"integrity": "sha512-JkaetveU7hFbqnAC1EV1sF4rlojU2D4Usc5CmS69l6NfmPDnpnFUegzFg33eDkkpNCxZ0mQp65HwUDrNFS/8MA=="
},
"has-to-string-tag-x": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz",
"integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==",
"requires": {
"has-symbol-support-x": "1.4.1"
}
},
"hashids": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/hashids/-/hashids-1.1.4.tgz",
@@ -584,11 +434,6 @@
"resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
"integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0="
},
"http-cache-semantics": {
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz",
"integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w=="
},
"http-signature": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
@@ -599,11 +444,6 @@
"sshpk": "1.13.1"
}
},
"indent-string": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-3.2.0.tgz",
"integrity": "sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok="
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -618,93 +458,16 @@
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"into-stream": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz",
"integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=",
"requires": {
"from2": "2.3.0",
"p-is-promise": "1.1.0"
}
},
"ip": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
"integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo="
},
"ip-regex": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
"integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk="
},
"is-ip": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-ip/-/is-ip-2.0.0.tgz",
"integrity": "sha1-aO6gfooKCpTC0IDdZ0xzGrKkYas=",
"requires": {
"ip-regex": "2.1.0"
}
},
"is-object": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz",
"integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA="
},
"is-online": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-online/-/is-online-7.0.0.tgz",
"integrity": "sha1-fiQIwK4efje6jVC9sjcmDTK/2W4=",
"requires": {
"got": "6.7.1",
"p-any": "1.1.0",
"p-timeout": "1.2.1",
"public-ip": "2.4.0"
}
},
"is-plain-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
"integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4="
},
"is-redirect": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
"integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ="
},
"is-retry-allowed": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz",
"integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ="
},
"is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
"isurl": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz",
"integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==",
"requires": {
"has-to-string-tag-x": "1.4.1",
"is-object": "1.0.1"
}
},
"js-yaml": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz",
@@ -720,11 +483,6 @@
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
"optional": true
},
"json-buffer": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
"integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg="
},
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
@@ -783,13 +541,10 @@
"nan": "2.8.0"
}
},
"keyv": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz",
"integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==",
"requires": {
"json-buffer": "3.0.0"
}
"lazy-val": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.2.tgz",
"integrity": "sha512-2BaSu6qVnicKdWQPysrffZVFAKcPcZQ/q2YyeSjAxWaJlvCvKSrkcvsSHlleeIfA//fW2goTcYDTy2cBLN7+PQ=="
},
"lodash.assign": {
"version": "4.2.0",
@@ -806,11 +561,6 @@
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
},
"lowercase-keys": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz",
"integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY="
},
"lru-cache": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
@@ -833,11 +583,6 @@
"mime-db": "1.30.0"
}
},
"mimic-response": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.0.tgz",
"integrity": "sha1-3z02Uqc/3ta5sLJBRub9BSNTRY4="
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
@@ -877,52 +622,11 @@
"mkdirp": "0.5.1"
}
},
"node-mac-notifier": {
"version": "0.0.13",
"resolved": "https://registry.npmjs.org/node-mac-notifier/-/node-mac-notifier-0.0.13.tgz",
"integrity": "sha1-1kt27RgfR5XURFui060Nb3KY9+I=",
"optional": true,
"requires": {
"bindings": "1.3.0",
"event-target-shim": "1.1.1",
"node-uuid": "1.4.8"
},
"dependencies": {
"node-uuid": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz",
"integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=",
"optional": true
}
}
},
"normalize-url": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz",
"integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==",
"requires": {
"prepend-http": "2.0.0",
"query-string": "5.1.0",
"sort-keys": "2.0.0"
},
"dependencies": {
"prepend-http": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
"integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc="
}
}
},
"oauth-sign": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
"integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM="
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -931,45 +635,6 @@
"wrappy": "1.0.2"
}
},
"p-any": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/p-any/-/p-any-1.1.0.tgz",
"integrity": "sha512-Ef0tVa4CZ5pTAmKn+Cg3w8ABBXh+hHO1aV8281dKOoUHfX+3tjG2EaFcC+aZyagg9b4EYGsHEjz21DnEE8Og2g==",
"requires": {
"p-some": "2.0.1"
}
},
"p-cancelable": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz",
"integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw=="
},
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
},
"p-is-promise": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz",
"integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4="
},
"p-some": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/p-some/-/p-some-2.0.1.tgz",
"integrity": "sha1-Zdh8ixVO289SIdFnd4ttLhUPbwY=",
"requires": {
"aggregate-error": "1.0.0"
}
},
"p-timeout": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz",
"integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=",
"requires": {
"p-finally": "1.0.0"
}
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -985,84 +650,11 @@
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
},
"prepend-http": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw="
},
"process-nextick-args": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw=="
},
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
},
"public-ip": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/public-ip/-/public-ip-2.4.0.tgz",
"integrity": "sha512-74cIy+T2cDmt+Z71AfVipH2q6qqZITPyNGszKV86OGDYIRvti1m8zg4GOaiTPCLgEIWnToKYXbhEnMiZWHPEUA==",
"requires": {
"dns-socket": "1.6.3",
"got": "8.1.0",
"is-ip": "2.0.0",
"pify": "3.0.0"
},
"dependencies": {
"got": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/got/-/got-8.1.0.tgz",
"integrity": "sha512-clILMRaLB1Ase3NWiSgTUrhpc951Z5V2IMtcFp8SKwu2aY+aeZZUuv/KKQmix+pz+Ov9SugLry6+JsBezHa9Vw==",
"requires": {
"@sindresorhus/is": "0.7.0",
"cacheable-request": "2.1.4",
"decompress-response": "3.3.0",
"duplexer3": "0.1.4",
"get-stream": "3.0.0",
"into-stream": "3.1.0",
"is-retry-allowed": "1.1.0",
"isurl": "1.0.0",
"lowercase-keys": "1.0.0",
"mimic-response": "1.0.0",
"p-cancelable": "0.3.0",
"p-timeout": "2.0.1",
"pify": "3.0.0",
"safe-buffer": "5.1.1",
"timed-out": "4.0.1",
"url-parse-lax": "3.0.0",
"url-to-options": "1.0.1"
}
},
"p-timeout": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz",
"integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==",
"requires": {
"p-finally": "1.0.0"
}
},
"pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
},
"prepend-http": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
"integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc="
},
"url-parse-lax": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz",
"integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=",
"requires": {
"prepend-http": "2.0.0"
}
}
}
},
"punycode": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
@@ -1073,30 +665,6 @@
"resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
"integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM="
},
"query-string": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.0.tgz",
"integrity": "sha512-F3DkxxlY0AqD/rwe4YAwjRE2HjOkKW7TxsuteyrS/Jbwrxw887PqYBL4sWUJ9D/V1hmFns0SCD6FDyvlwo9RCQ==",
"requires": {
"decode-uri-component": "0.2.0",
"object-assign": "4.1.1",
"strict-uri-encode": "1.1.0"
}
},
"readable-stream": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.4.tgz",
"integrity": "sha512-vuYxeWYM+fde14+rajzqgeohAI7YoJcHE7kXDAc4Nk0EbuKnJfqtY9YtRkLo/tqkuF7MsBQRhPnPeyjYITp3ZQ==",
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "1.0.0",
"process-nextick-args": "2.0.0",
"safe-buffer": "5.1.1",
"string_decoder": "1.0.3",
"util-deprecate": "1.0.2"
}
},
"request": {
"version": "2.81.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz",
@@ -1126,14 +694,6 @@
"uuid": "3.1.0"
}
},
"responselike": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
"integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=",
"requires": {
"lowercase-keys": "1.0.0"
}
},
"rimraf": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
@@ -1186,7 +746,7 @@
"semver": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
"integrity": "sha1-4FnAnYVx8FQII3M0M1BdOi8AsY4="
"integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg=="
},
"sntp": {
"version": "1.0.9",
@@ -1196,12 +756,17 @@
"hoek": "2.16.3"
}
},
"sort-keys": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz",
"integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=",
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"source-map-support": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.0.tgz",
"integrity": "sha512-vUoN3I7fHQe0R/SJLKRdKYuEdRGogsviXFkHHo17AWaTGv17VLnxw+CFXvqy+y4ORZ3doWLQcxRYfwKrsd/H7Q==",
"requires": {
"is-plain-obj": "1.1.0"
"source-map": "0.6.1"
}
},
"spawn-rx": {
@@ -1241,19 +806,6 @@
}
}
},
"strict-uri-encode": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
"integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM="
},
"string_decoder": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
"requires": {
"safe-buffer": "5.1.1"
}
},
"stringstream": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
@@ -1264,11 +816,6 @@
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.1.0.tgz",
"integrity": "sha512-dQoid9tqQ+uotGhuTKEY11X4xhyYePVnqGSoSm3OGKh2E8LZ6RPULp1uXTctk33IeERlrRJYoVSBglsL05F5Uw=="
},
"timed-out": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
"integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8="
},
"tough-cookie": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz",
@@ -1306,29 +853,6 @@
"resolved": "https://registry.npmjs.org/untildify/-/untildify-3.0.2.tgz",
"integrity": "sha1-fx8wIFWz/qDz6B3HjrNnZstl4/E="
},
"unzip-response": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz",
"integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c="
},
"url-parse-lax": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz",
"integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=",
"requires": {
"prepend-http": "1.0.4"
}
},
"url-to-options": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz",
"integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"uuid": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz",

View File

@@ -1,7 +1,7 @@
{
"name": "zulip",
"productName": "Zulip",
"version": "1.8.2",
"version": "1.7.0",
"description": "Zulip Desktop App",
"license": "Apache-2.0",
"copyright": "Kandra Labs, Inc.",
@@ -30,15 +30,10 @@
"electron-is-dev": "0.3.0",
"electron-log": "2.2.7",
"electron-spellchecker": "1.1.2",
"electron-updater": "2.18.2",
"electron-window-state": "4.1.1",
"is-online": "7.0.0",
"electron-updater": "2.16.2",
"node-json-db": "0.7.3",
"request": "2.81.0",
"semver": "5.4.1",
"wurl": "2.5.0"
},
"optionalDependencies": {
"node-mac-notifier": "0.0.13"
}
}

View File

@@ -13,7 +13,7 @@ body {
#content {
display: flex;
height: 100%;
background: #fff url(../img/ic_loading.gif) no-repeat;
background: #eee url(../img/ic_loading.gif) no-repeat;
background-size: 60px 60px;
background-position: center;
}
@@ -81,18 +81,11 @@ body {
text-rendering: optimizeLegibility;
}
#actions-container {
display: flex;
flex-direction: column;
position: fixed;
bottom: 0;
width: inherit;
}
.action-button {
display: flex;
flex-direction: column;
padding: 12px;
align-items: center;
padding: 10px;
}
.action-button:hover {
@@ -108,26 +101,6 @@ body {
color: #98a9b3;
}
.action-button.disable {
opacity: 0.6;
}
.action-button.disable:hover {
cursor: not-allowed;
}
.action-button.disable:hover i {
color: #6c8592;
}
.action-button.active {
background-color: rgba(255, 255, 255, 0.25);
}
.action-button.active i {
color: #eee;
}
.tab:first-child {
margin-top: 8px;
}
@@ -250,7 +223,7 @@ body {
webview {
opacity: 1;
/* transition: opacity 0.3s ease-in; */
transition: opacity 0.3s ease-in;
flex-grow: 1;
}
@@ -273,7 +246,6 @@ webview:focus {
/* Tooltip styling */
#back-tooltip,
#reload-tooltip,
#setting-tooltip {
font-family: sans-serif;
@@ -290,7 +262,6 @@ webview:focus {
font-size: 14px;
}
#back-tooltip:after,
#reload-tooltip:after,
#setting-tooltip:after {
content: " ";
@@ -391,4 +362,4 @@ webview:focus {
overflow: hidden;
opacity: 1;
}
}
}

View File

@@ -12,19 +12,16 @@ body {
}
kbd {
display: inline-block;
border: 1px solid #ccc ;
border-radius: 3px;
padding: 0.3em 0.8em;
border: 1px solid #ccc;
font-size: 15px;
font-family: Courier New, Courier, monospace;
background-color: #383430;
color: #ededed;
display: inline-block;
margin: 0 0.1em;
font-weight: bold;
white-space: nowrap;
background-color: #f7f7f7;
color: #333;
margin: 0 0.1em;
padding: 0.3em 0.8em;
text-shadow: 0 1px 0 #fff;
line-height: 1.4;
}
table,
@@ -41,11 +38,11 @@ table {
}
table tr:nth-child(even) {
background-color: #fafafa;
background-color: #f7eee6;
}
table tr:nth-child(odd) {
background-color: #fff;
background-color: #fff8ef;
}
td {
@@ -266,13 +263,7 @@ img.server-info-icon {
margin: 10px 0 20px 0;
background: #fff;
width: 70%;
transition: all 0.2s;
}
.settings-card:hover {
border-left: 8px solid #bcbcbc;
box-shadow: 0 2px 5px 0 rgba(0,0,0,0.16),
0 2px 0px 0px rgba(0,0,0,0.12);
}
.hidden {
@@ -337,11 +328,13 @@ i.open-tab-button {
}
#server-info-container {
min-height: calc(100% - 260px);
min-height: calc(100% - 235px);
}
#create-organization-container {
font-size: 1.15em;
position: fixed;
bottom: 15px;
}
#create-organization-container i {
@@ -397,6 +390,7 @@ input.toggle-round+label:before {
right: 2px;
background-color: #f1f1f1;
border-radius: 25px;
transition: background 0.4s;
}
input.toggle-round+label:after {
@@ -404,6 +398,7 @@ input.toggle-round+label:after {
height: 25px;
background-color: #fff;
border-radius: 100%;
transition: margin 0.4s;
}
input.toggle-round:checked+label:before {

View File

@@ -4,7 +4,7 @@ const Tab = require(__dirname + '/../components/tab.js');
class FunctionalTab extends Tab {
template() {
return `<div class="tab functional-tab" data-tab-id="${this.props.tabIndex}">
return `<div class="tab functional-tab">
<div class="server-tab-badge close-button">
<i class="material-icons">close</i>
</div>
@@ -16,11 +16,10 @@ class FunctionalTab extends Tab {
init() {
this.$el = this.generateNodeFromTemplate(this.template());
if (this.props.name !== 'Settings') {
this.props.$root.appendChild(this.$el);
this.$closeButton = this.$el.getElementsByClassName('server-tab-badge')[0];
this.registerListeners();
}
this.props.$root.appendChild(this.$el);
this.$closeButton = this.$el.getElementsByClassName('server-tab-badge')[0];
this.registerListeners();
}
registerListeners() {

View File

@@ -7,7 +7,7 @@ const {ipcRenderer} = require('electron');
class ServerTab extends Tab {
template() {
return `<div class="tab" data-tab-id="${this.props.tabIndex}">
return `<div class="tab">
<div class="server-tooltip" style="display:none"></div>
<div class="server-tab-badge"></div>
<div class="server-tab">
@@ -50,8 +50,7 @@ class ServerTab extends Tab {
shortcutText = `Ctrl+${shownIndex}`;
}
// Array index == Shown index - 1
ipcRenderer.send('switch-server-tab', shownIndex - 1);
ipcRenderer.send('register-server-tab-shortcut', shownIndex);
return shortcutText;
}

View File

@@ -4,14 +4,12 @@ const path = require('path');
const fs = require('fs');
const DomainUtil = require(__dirname + '/../utils/domain-util.js');
const ConfigUtil = require(__dirname + '/../utils/config-util.js');
const SystemUtil = require(__dirname + '/../utils/system-util.js');
const LinkUtil = require(__dirname + '/../utils/link-util.js');
const { shell, app } = require('electron').remote;
const BaseComponent = require(__dirname + '/../components/base.js');
const shouldSilentWebview = ConfigUtil.getConfigItem('silent');
class WebView extends BaseComponent {
constructor(props) {
super();
@@ -26,7 +24,6 @@ class WebView extends BaseComponent {
template() {
return `<webview
class="disabled"
data-tab-id="${this.props.tabIndex}"
src="${this.props.url}"
${this.props.nodeIntegration ? 'nodeIntegration' : ''}
disablewebsecurity
@@ -57,30 +54,12 @@ class WebView extends BaseComponent {
}
});
if (shouldSilentWebview) {
this.$el.addEventListener('dom-ready', () => {
this.$el.setAudioMuted(true);
});
}
this.$el.addEventListener('page-title-updated', event => {
const { title } = event;
this.badgeCount = this.getBadgeCount(title);
this.props.onTitleChange();
});
this.$el.addEventListener('did-navigate-in-page', event => {
const isSettingPage = event.url.includes('renderer/preference.html');
if (isSettingPage) {
return;
}
this.canGoBackButton();
});
this.$el.addEventListener('did-navigate', () => {
this.canGoBackButton();
});
this.$el.addEventListener('page-favicon-updated', event => {
const { favicons } = event;
// This returns a string of favicons URL. If there is a PM counts in unread messages then the URL would be like
@@ -195,15 +174,6 @@ class WebView extends BaseComponent {
}
}
canGoBackButton() {
const $backButton = document.querySelector('#actions-container #back-action');
if (this.$el.canGoBack()) {
$backButton.classList.remove('disable');
} else {
$backButton.classList.add('disable');
}
}
forward() {
if (this.$el.canGoForward()) {
this.$el.goForward();

View File

@@ -0,0 +1,69 @@
const NodeConsole = require('console').Console;
const fs = require('fs');
const { app } = require('electron').remote;
const isDev = require('electron-is-dev');
const browserConsole = console;
const logDir = `${app.getPath('userData')}/Logs`;
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir);
}
function customConsole(opts, type, ...args) {
const { nodeConsole, timestamp } = opts;
if (timestamp) {
args.unshift(timestamp());
}
if (!isDev) {
const nodeConsoleLog = nodeConsole[type] || nodeConsole.log;
nodeConsoleLog.apply(null, args);
}
browserConsole[type].apply(null, args);
}
function getTimestamp() {
const date = new Date();
const timestamp =
`${date.getMonth()}/${date.getDate()} ` +
`${date.getMinutes()}:${date.getSeconds()}`;
return timestamp;
}
function setConsoleProto(type) {
Object.defineProperty(this, type, {
value(...args) {
const { timestamp, nodeConsole } = this;
const opts = {
timestamp,
nodeConsole
};
customConsole.apply(null, [].concat(opts, type, args));
}
});
}
class Console {
constructor(opts = {}) {
let { timestamp, file } = opts;
file = `${logDir}/${file || 'console.log'}`;
if (timestamp === true) {
timestamp = getTimestamp;
}
const fileStream = fs.createWriteStream(file);
const nodeConsole = new NodeConsole(fileStream);
this.nodeConsole = nodeConsole;
this.timestamp = timestamp;
this.setUpConsole();
}
setUpConsole() {
for (const type in browserConsole) {
setConsoleProto.call(this, type);
}
}
}
module.exports = Console;

View File

@@ -1,17 +1,15 @@
'use strict';
require(__dirname + '/js/tray.js');
const { ipcRenderer, remote } = require('electron');
const isDev = require('electron-is-dev');
const { session } = remote;
require(__dirname + '/js/tray.js');
const DomainUtil = require(__dirname + '/js/utils/domain-util.js');
const WebView = require(__dirname + '/js/components/webview.js');
const ServerTab = require(__dirname + '/js/components/server-tab.js');
const FunctionalTab = require(__dirname + '/js/components/functional-tab.js');
const ConfigUtil = require(__dirname + '/js/utils/config-util.js');
const ReconnectUtil = require(__dirname + '/js/utils/reconnect-util.js');
class ServerManagerView {
constructor() {
@@ -22,13 +20,11 @@ class ServerManagerView {
this.$reloadButton = $actionsContainer.querySelector('#reload-action');
this.$settingsButton = $actionsContainer.querySelector('#settings-action');
this.$webviewsContainer = document.getElementById('webviews-container');
this.$backButton = $actionsContainer.querySelector('#back-action');
this.$addServerTooltip = document.getElementById('add-server-tooltip');
this.$reloadTooltip = $actionsContainer.querySelector('#reload-tooltip');
this.$settingsTooltip = $actionsContainer.querySelector('#setting-tooltip');
this.$serverIconTooltip = document.getElementsByClassName('server-tooltip');
this.$backTooltip = $actionsContainer.querySelector('#back-tooltip');
this.$sidebar = document.getElementById('sidebar');
@@ -39,7 +35,6 @@ class ServerManagerView {
this.activeTabIndex = -1;
this.tabs = [];
this.functionalTabs = {};
this.tabIndex = 0;
}
init() {
@@ -82,7 +77,6 @@ class ServerManagerView {
showSidebar: true,
badgeOption: true,
startAtLogin: false,
startMinimized: false,
enableSpellchecker: true,
showNotification: true,
betaUpdate: false,
@@ -90,13 +84,6 @@ class ServerManagerView {
lastActiveTab: 0
};
// Platform specific settings
if (process.platform === 'win32') {
// Only available on Windows
settingOptions.flashTaskbarOnMessage = true;
}
for (const i in settingOptions) {
if (ConfigUtil.getConfigItem(i) === null) {
ConfigUtil.setConfigItem(i, settingOptions[i]);
@@ -119,28 +106,23 @@ class ServerManagerView {
}
// Open last active tab
this.activateTab(ConfigUtil.getConfigItem('lastActiveTab'));
// Remove focus from the settings icon at sidebar bottom
this.$settingsButton.classList.remove('active');
} else {
this.openSettings('Servers');
}
}
initServer(server, index) {
const tabIndex = this.getTabIndex();
this.tabs.push(new ServerTab({
role: 'server',
icon: server.icon,
$root: this.$tabsContainer,
onClick: this.activateLastTab.bind(this, index),
index,
tabIndex,
onHover: this.onHover.bind(this, index, server.alias),
onHoverOut: this.onHoverOut.bind(this, index),
webview: new WebView({
$root: this.$webviewsContainer,
index,
tabIndex,
url: server.url,
name: server.alias,
isActive: () => {
@@ -164,27 +146,10 @@ class ServerManagerView {
this.$settingsButton.addEventListener('click', () => {
this.openSettings('General');
});
this.$backButton.addEventListener('click', () => {
this.tabs[this.activeTabIndex].webview.back();
});
const $serverImgs = document.querySelectorAll('.server-icons');
$serverImgs.forEach($serverImg => {
$serverImg.addEventListener('error', () => {
$serverImg.src = 'img/icon.png';
});
});
this.sidebarHoverEvent(this.$addServerButton, this.$addServerTooltip);
this.sidebarHoverEvent(this.$settingsButton, this.$settingsTooltip);
this.sidebarHoverEvent(this.$reloadButton, this.$reloadTooltip);
this.sidebarHoverEvent(this.$backButton, this.$backTooltip);
}
getTabIndex() {
const currentIndex = this.tabIndex;
this.tabIndex++;
return currentIndex;
}
sidebarHoverEvent(SidebarButton, SidebarTooltip) {
@@ -213,20 +178,16 @@ class ServerManagerView {
this.functionalTabs[tabProps.name] = this.tabs.length;
const tabIndex = this.getTabIndex();
this.tabs.push(new FunctionalTab({
role: 'function',
materialIcon: tabProps.materialIcon,
name: tabProps.name,
$root: this.$tabsContainer,
index: this.functionalTabs[tabProps.name],
tabIndex,
onClick: this.activateTab.bind(this, this.functionalTabs[tabProps.name]),
onDestroy: this.destroyTab.bind(this, tabProps.name, this.functionalTabs[tabProps.name]),
webview: new WebView({
$root: this.$webviewsContainer,
index: this.functionalTabs[tabProps.name],
tabIndex,
url: tabProps.url,
name: tabProps.name,
isActive: () => {
@@ -238,6 +199,7 @@ class ServerManagerView {
preload: false
})
}));
this.activateTab(this.functionalTabs[tabProps.name]);
}
@@ -247,7 +209,6 @@ class ServerManagerView {
materialIcon: 'settings',
url: `file://${__dirname}/preference.html#${nav}`
});
this.$settingsButton.classList.add('active');
this.tabs[this.functionalTabs.Settings].webview.send('switch-settings-nav', nav);
}
@@ -283,19 +244,10 @@ class ServerManagerView {
if (this.activeTabIndex === index) {
return;
} else if (hideOldTab) {
// If old tab is functional tab Settings, remove focus from the settings icon at sidebar bottom
if (this.tabs[this.activeTabIndex].props.role === 'function' && this.tabs[this.activeTabIndex].props.name === 'Settings') {
this.$settingsButton.classList.remove('active');
}
this.tabs[this.activeTabIndex].deactivate();
}
}
try {
this.tabs[index].webview.canGoBackButton();
} catch (err) {
}
this.activeTabIndex = index;
this.tabs[index].activate();
@@ -303,27 +255,6 @@ class ServerManagerView {
tabs: this.tabs,
activeTabIndex: this.activeTabIndex
});
ipcRenderer.on('toggle-sidebar', (event, state) => {
const selector = 'webview:not([class*=disabled])';
const webview = document.querySelector(selector);
const webContents = webview.getWebContents();
webContents.send('toggle-sidebar', state);
});
ipcRenderer.on('toogle-silent', (event, state) => {
const webviews = document.querySelectorAll('webview');
webviews.forEach(webview => {
try {
webview.setAudioMuted(state);
} catch (err) {
// webview is not ready yet
webview.addEventListener('dom-ready', () => {
webview.isAudioMuted();
});
}
});
});
}
destroyTab(name, index) {
@@ -432,7 +363,7 @@ class ServerManagerView {
});
ipcRenderer.on('switch-server-tab', (event, index) => {
this.activateLastTab(index);
this.activateTab(index);
});
ipcRenderer.on('reload-proxy', (event, showAlert) => {
@@ -456,18 +387,6 @@ class ServerManagerView {
this.$fullscreenPopup.classList.remove('show');
});
ipcRenderer.on('focus-webview-with-id', (event, webviewId) => {
const webviews = document.querySelectorAll('webview');
webviews.forEach(webview => {
const currentId = webview.getWebContents().id;
const tabId = webview.getAttribute('data-tab-id');
const concurrentTab = document.querySelector(`div[data-tab-id="${tabId}"]`);
if (currentId === webviewId) {
concurrentTab.click();
}
});
});
ipcRenderer.on('render-taskbar-icon', (event, messageCount) => {
// Create a canvas from unread messagecounts
function createOverlayIcon(messageCount) {
@@ -501,24 +420,9 @@ class ServerManagerView {
window.onload = () => {
const serverManagerView = new ServerManagerView();
const reconnectUtil = new ReconnectUtil(serverManagerView);
serverManagerView.init();
window.addEventListener('online', () => {
reconnectUtil.pollInternetAndReload();
serverManagerView.reloadView();
});
window.addEventListener('offline', () => {
reconnectUtil.clearState();
console.log('No internet connection, you are offline.');
});
// only start electron-connect (auto reload on change) when its ran
// from `npm run dev` or `gulp dev` and not from `npm start` when
// app is started `npm start` main process's proces.argv will have
// `--no-electron-connect`
const mainProcessArgv = remote.getGlobal('process').argv;
if (isDev && !mainProcessArgv.includes('--no-electron-connect')) {
const electronConnect = require('electron-connect');
electronConnect.client.create();
}
};

View File

@@ -0,0 +1,30 @@
'use strict';
const { remote } = require('electron');
const ConfigUtil = require(__dirname + '/utils/config-util.js');
const app = remote.app;
// From https://github.com/felixrieseberg/electron-windows-notifications#appusermodelid
// On windows 8 we have to explicitly set the appUserModelId otherwise notification won't work.
app.setAppUserModelId('org.zulip.zulip-electron');
const NativeNotification = window.Notification;
class baseNotification extends NativeNotification {
constructor(title, opts) {
opts.silent = ConfigUtil.getConfigItem('silent') || false;
super(title, opts);
}
static requestPermission() {
return; // eslint-disable-line no-useless-return
}
// Override default Notification permission
static get permission() {
return ConfigUtil.getConfigItem('showNotification') ? 'granted' : 'denied';
}
}
window.Notification = baseNotification;

View File

@@ -1,100 +0,0 @@
'use strict';
const { ipcRenderer } = require('electron');
const url = require('url');
const MacNotifier = require('node-mac-notifier');
const ConfigUtil = require('../utils/config-util');
const {
appId, customReply, focusCurrentServer, parseReply, setupReply
} = require('./helpers');
let replyHandler;
let clickHandler;
class DarwinNotification {
constructor(title, opts) {
const silent = ConfigUtil.getConfigItem('silent') || false;
const { host, protocol } = location;
const { icon } = opts;
const profilePic = url.resolve(`${protocol}//${host}`, icon);
this.tag = opts.tag;
const notification = new MacNotifier(title, Object.assign(opts, {
bundleId: appId,
canReply: true,
silent,
icon: profilePic
}));
notification.addEventListener('click', () => {
// focus to the server who sent the
// notification if not focused already
if (clickHandler) {
clickHandler();
}
focusCurrentServer();
ipcRenderer.send('focus-app');
});
notification.addEventListener('reply', this.notificationHandler);
}
static requestPermission() {
return; // eslint-disable-line no-useless-return
}
// Override default Notification permission
static get permission() {
return ConfigUtil.getConfigItem('showNotification') ? 'granted' : 'denied';
}
set onreply(handler) {
replyHandler = handler;
}
get onreply() {
return replyHandler;
}
set onclick(handler) {
clickHandler = handler;
}
get onclick() {
return clickHandler;
}
// not something that is common or
// used by zulip server but added to be
// future proff.
addEventListener(event, handler) {
if (event === 'click') {
clickHandler = handler;
}
if (event === 'reply') {
replyHandler = handler;
}
}
notificationHandler({ response }) {
response = parseReply(response);
focusCurrentServer();
setupReply(this.tag);
if (replyHandler) {
replyHandler(response);
return;
}
customReply(response);
}
// method specific to notification api
// used by zulip
close() {
return; // eslint-disable-line no-useless-return
}
}
module.exports = DarwinNotification;

View File

@@ -1,31 +0,0 @@
'use strict';
const { ipcRenderer } = require('electron');
const ConfigUtil = require('../utils/config-util');
const { focusCurrentServer } = require('./helpers');
const NativeNotification = window.Notification;
class BaseNotification extends NativeNotification {
constructor(title, opts) {
opts.silent = true;
super(title, opts);
this.addEventListener('click', () => {
// focus to the server who sent the
// notification if not focused already
focusCurrentServer();
ipcRenderer.send('focus-app');
});
}
static requestPermission() {
return; // eslint-disable-line no-useless-return
}
// Override default Notification permission
static get permission() {
return ConfigUtil.getConfigItem('showNotification') ? 'granted' : 'denied';
}
}
module.exports = BaseNotification;

View File

@@ -1,149 +0,0 @@
const { remote } = require('electron');
// Do not change this
const appId = 'org.zulip.zulip-electron';
const botsList = [];
let botsListLoaded = false;
// this function load list of bots from the server
// sync=True for a synchronous getJSON request
// in case botsList isn't already completely loaded when required in parseRely
function loadBots(sync = false) {
const { $ } = window;
botsList.length = 0;
if (sync) {
$.ajaxSetup({async: false});
}
$.getJSON('/json/users')
.done(data => {
const members = data.members;
members.forEach(membersRow => {
if (membersRow.is_bot) {
const bot = `@${membersRow.full_name}`;
const mention = `@**${bot.replace(/^@/, '')}**`;
botsList.push([bot, mention]);
}
});
botsListLoaded = true;
})
.fail(error => {
console.log('Request failed: ', error.responseText);
console.log('Request status: ', error.statusText);
});
if (sync) {
$.ajaxSetup({async: true});
}
}
function checkElements(...elements) {
let status = true;
elements.forEach(element => {
if (element === null || element === undefined) {
status = false;
}
});
return status;
}
function customReply(reply) {
// server does not support notification reply yet.
const buttonSelector = '.messagebox #send_controls button[type=submit]';
const messageboxSelector = '.selected_message .messagebox .messagebox-border .messagebox-content';
const textarea = document.querySelector('#compose-textarea');
const messagebox = document.querySelector(messageboxSelector);
const sendButton = document.querySelector(buttonSelector);
// sanity check for old server versions
const elementsExists = checkElements(textarea, messagebox, sendButton);
if (!elementsExists) {
return;
}
textarea.value = reply;
messagebox.click();
sendButton.click();
}
const currentWindow = remote.getCurrentWindow();
const webContents = remote.getCurrentWebContents();
const webContentsId = webContents.id;
// this function will focus the server that sent
// the notification. Main function implemented in main.js
function focusCurrentServer() {
currentWindow.send('focus-webview-with-id', webContentsId);
}
// this function parses the reply from to notification
// making it easier to reply from notification eg
// @username in reply will be converted to @**username**
// #stream in reply will be converted to #**stream**
// bot mentions are not yet supported
function parseReply(reply) {
const usersDiv = document.querySelectorAll('#user_presences li');
const streamHolder = document.querySelectorAll('#stream_filters li');
const users = [];
const streams = [];
usersDiv.forEach(userRow => {
const anchor = userRow.querySelector('span a');
if (anchor !== null) {
const user = `@${anchor.textContent.trim()}`;
const mention = `@**${user.replace(/^@/, '')}**`;
users.push([user, mention]);
}
});
streamHolder.forEach(stream => {
const streamAnchor = stream.querySelector('div a');
if (streamAnchor !== null) {
const streamName = `#${streamAnchor.textContent.trim()}`;
const streamMention = `#**${streamName.replace(/^#/, '')}**`;
streams.push([streamName, streamMention]);
}
});
users.forEach(([user, mention]) => {
if (reply.includes(user)) {
const regex = new RegExp(user, 'g');
reply = reply.replace(regex, mention);
}
});
streams.forEach(([stream, streamMention]) => {
const regex = new RegExp(stream, 'g');
reply = reply.replace(regex, streamMention);
});
// If botsList isn't completely loaded yet, make a synchronous getJSON request for list
if (botsListLoaded === false) {
loadBots(true);
}
// Iterate for every bot name and replace in reply
// @botname with @**botname**
botsList.forEach(([bot, mention]) => {
if (reply.includes(bot)) {
const regex = new RegExp(bot, 'g');
reply = reply.replace(regex, mention);
}
});
reply = reply.replace(/\\n/, '\n');
return reply;
}
function setupReply(id) {
const { narrow } = window;
narrow.by_subject(id, { trigger: 'notification' });
}
module.exports = {
appId,
checkElements,
customReply,
parseReply,
setupReply,
focusCurrentServer,
loadBots
};

View File

@@ -1,27 +0,0 @@
'use strict';
const {
remote: { app }
} = require('electron');
const DefaultNotification = require('./default-notification');
const { appId, loadBots } = require('./helpers');
// From https://github.com/felixrieseberg/electron-windows-notifications#appusermodelid
// On windows 8 we have to explicitly set the appUserModelId otherwise notification won't work.
app.setAppUserModelId(appId);
window.Notification = DefaultNotification;
if (process.platform === 'darwin') {
const DarwinNotification = require('./darwin-notifications');
window.Notification = DarwinNotification;
}
window.addEventListener('load', () => {
// Call this function only when user is logged in
// eslint-disable-next-line no-undef, camelcase
if (page_params.realm_uri) {
loadBots();
}
});

View File

@@ -1,10 +1,11 @@
'use strict';
const path = require('path');
const { ipcRenderer, remote } = require('electron');
const { ipcRenderer } = require('electron');
const { app, dialog } = require('electron').remote;
const fs = require('fs-extra');
const { app, dialog } = remote;
const currentBrowserWindow = remote.getCurrentWindow();
const BaseSection = require(__dirname + '/base-section.js');
const ConfigUtil = require(__dirname + '/../../utils/config-util.js');
@@ -60,14 +61,10 @@ class GeneralSection extends BaseSection {
<div class="setting-description">Start app at login</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="start-minimize-option">
<div class="setting-description">Always start minimized</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="enable-spellchecker-option">
<div class="setting-description">Enable Spellchecker (requires restart)</div>
<div class="setting-control"></div>
</div>
<div class="setting-description">Enable Spellchecker (requires restart)</div>
<div class="setting-control"></div>
</div>
</div>
<div class="title">Reset Application Data</div>
<div class="settings-card">
@@ -92,7 +89,6 @@ class GeneralSection extends BaseSection {
this.updateResetDataOption();
this.showDesktopNotification();
this.enableSpellchecker();
this.minimizeOnStart();
// Platform specific settings
// Flashing taskbar on Windows
@@ -159,7 +155,6 @@ class GeneralSection extends BaseSection {
const newValue = !ConfigUtil.getConfigItem('silent', true);
ConfigUtil.setConfigItem('silent', newValue);
this.updateSilentOption();
currentBrowserWindow.send('toogle-silent', newValue);
}
});
}
@@ -239,18 +234,6 @@ class GeneralSection extends BaseSection {
});
}
minimizeOnStart() {
this.generateSettingOption({
$element: document.querySelector('#start-minimize-option .setting-control'),
value: ConfigUtil.getConfigItem('startMinimized', false),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('startMinimized');
ConfigUtil.setConfigItem('startMinimized', newValue);
this.minimizeOnStart();
}
});
}
}
module.exports = GeneralSection;

View File

@@ -13,9 +13,9 @@ class NewServerForm extends BaseComponent {
return `
<div class="settings-card">
<div class="server-info-right">
<div class="title">URL of Zulip organization</div>
<div class="title">URL of your Zulip organization</div>
<div class="server-info-row">
<input class="setting-input-value" autofocus placeholder="your-organization.zulipchat.com or chat.your-organization.com"/>
<input class="setting-input-value" autofocus placeholder="acme.zulipchat.com"/>
</div>
<div class="server-info-row">
<div class="action blue server-save-action">
@@ -43,13 +43,11 @@ class NewServerForm extends BaseComponent {
}
submitFormHandler() {
this.$saveServerButton.children[1].innerHTML = 'Adding...';
DomainUtil.checkDomain(this.$newServerUrl.value).then(serverConf => {
DomainUtil.addDomain(serverConf).then(() => {
this.props.onChange(this.props.index);
});
}, errorMessage => {
this.$saveServerButton.children[1].innerHTML = 'Add';
alert(errorMessage);
});
}

View File

@@ -69,27 +69,10 @@ class PreferenceView extends BaseComponent {
window.location.hash = `#${navItem}`;
}
// Handle toggling and reflect changes in preference page
handleToggle(elementName, state) {
const inputSelector = `#${elementName} .action .switch input`;
const input = document.querySelector(inputSelector);
if (input) {
input.checked = state;
}
}
registerIpcs() {
ipcRenderer.on('switch-settings-nav', (event, navItem) => {
this.handleNavigation(navItem);
});
ipcRenderer.on('toggle-sidebar', (event, state) => {
this.handleToggle('sidebar-option', state);
});
ipcRenderer.on('toggletray', (event, state) => {
this.handleToggle('tray-option', state);
});
}
}

View File

@@ -109,7 +109,7 @@ class ShortcutsSection extends BaseSection {
<td>Enter Full Screen</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>+</kbd></td>
<td><kbd>${userOSKey}</kbd><kbd>=</kbd></td>
<td>Zoom In</td>
</tr>
<tr>

View File

@@ -197,16 +197,13 @@ ipcRenderer.on('tray', (event, arg) => {
});
function toggleTray() {
let state;
if (window.tray) {
state = false;
window.tray.destroy();
if (window.tray.isDestroyed()) {
window.tray = null;
}
ConfigUtil.setConfigItem('trayIcon', false);
} else {
state = true;
createTray();
if (process.platform === 'linux' || process.platform === 'win32') {
renderNativeImage(unread).then(image => {
@@ -216,10 +213,6 @@ function toggleTray() {
}
ConfigUtil.setConfigItem('trayIcon', true);
}
const selector = 'webview:not([class*=disabled])';
const webview = document.querySelector(selector);
const webContents = webview.getWebContents();
webContents.send('toggletray', state);
}
ipcRenderer.on('toggletray', toggleTray);

View File

@@ -1,29 +1,16 @@
'use strict';
const fs = require('fs');
const path = require('path');
const process = require('process');
const JsonDB = require('node-json-db');
const Logger = require('./logger-util');
const logger = new Logger({
file: 'config-util.log',
timestamp: true
});
let instance = null;
let dialog = null;
let app = null;
/* To make the util runnable in both main and renderer process */
if (process.type === 'renderer') {
const remote = require('electron').remote;
dialog = remote.dialog;
app = remote.app;
app = require('electron').remote.app;
} else {
const electron = require('electron');
dialog = electron.dialog;
app = electron.app;
app = require('electron').app;
}
class ConfigUtil {
@@ -60,22 +47,7 @@ class ConfigUtil {
}
reloadDB() {
const settingsJsonPath = path.join(app.getPath('userData'), '/settings.json');
try {
const file = fs.readFileSync(settingsJsonPath, 'utf8');
JSON.parse(file);
} catch (err) {
if (fs.existsSync(settingsJsonPath)) {
fs.unlinkSync(settingsJsonPath);
dialog.showErrorBox(
'Error saving settings',
'We encountered error while saving current settings.'
);
logger.error('Error while JSON parsing settings.json: ');
logger.error(err);
}
}
this.db = new JsonDB(settingsJsonPath, true, true);
this.db = new JsonDB(app.getPath('userData') + '/settings.json', true, true);
}
}

View File

@@ -1,31 +0,0 @@
const fs = require('fs');
let app = null;
let setupCompleted = false;
if (process.type === 'renderer') {
app = require('electron').remote.app;
} else {
app = require('electron').app;
}
const zulipDir = app.getPath('userData');
const logDir = `${zulipDir}/Logs/`;
const initSetUp = () => {
// if it is the first time the app is running
// create zulip dir in userData folder to
// avoid errors
if (!setupCompleted) {
if (!fs.existsSync(zulipDir)) {
fs.mkdirSync(zulipDir);
}
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir);
}
setupCompleted = true;
}
};
module.exports = {
initSetUp
};

View File

@@ -5,12 +5,6 @@ const fs = require('fs');
const path = require('path');
const JsonDB = require('node-json-db');
const request = require('request');
const Logger = require('./logger-util');
const logger = new Logger({
file: `domain-util.log`,
timestamp: true
});
let instance = null;
@@ -99,7 +93,8 @@ class DomainUtil {
checkDomain(domain, silent = false) {
if (!silent && this.duplicateDomain(domain)) {
// Do not check duplicate in silent mode
return Promise.reject('This server has been added.');
alert('This server has been added.');
return;
}
domain = this.formatUrl(domain);
@@ -120,16 +115,11 @@ class DomainUtil {
'Error: unable to verify the first certificate',
'Error: unable to get local issuer certificate'
];
// If the domain contains following strings we just bypass the server
const whitelistDomains = [
'zulipdev.org'
];
// make sure that error is a error or string not undefined
// so validation does not throw error.
error = error || '';
if (!error && response.statusCode < 400) {
if (!error && response.statusCode !== 404) {
// Correct
this.getServerSettings(domain).then(serverSettings => {
resolve(serverSettings);
@@ -169,7 +159,7 @@ class DomainUtil {
}
} else {
const invalidZulipServerError = `${domain} does not appear to be a valid Zulip server. Make sure that \
\n(1) you can connect to that URL in a web browser and \n (2) if you need a proxy to connect to the Internet, that you've configured your proxy in the Network settings \n (3) its a zulip server`;
\n(1) you can connect to that URL in a web browser and \n (2) if you need a proxy to connect to the Internet, that you've configured your proxy in the Network settings`;
reject(invalidZulipServerError);
}
});
@@ -235,23 +225,7 @@ class DomainUtil {
}
reloadDB() {
const domainJsonPath = path.join(app.getPath('userData'), '/domain.json');
try {
const file = fs.readFileSync(domainJsonPath, 'utf8');
JSON.parse(file);
} catch (err) {
if (fs.existsSync(domainJsonPath)) {
fs.unlinkSync(domainJsonPath);
dialog.showErrorBox(
'Error saving new organization',
'There seems to be error while saving new organization, ' +
'you may have to re-add your previous organizations back.'
);
logger.error('Error while JSON parsing domain.json: ');
logger.error(err);
}
}
this.db = new JsonDB(domainJsonPath, true, true);
this.db = new JsonDB(app.getPath('userData') + '/domain.json', true, true);
}
generateFilePath(url) {

View File

@@ -1,74 +0,0 @@
'use strict';
const fs = require('fs');
const path = require('path');
const process = require('process');
const remote =
process.type === 'renderer' ? require('electron').remote : require('electron');
const JsonDB = require('node-json-db');
const Logger = require('./logger-util');
const logger = new Logger({
file: 'linux-update-util.log',
timestamp: true
});
/* To make the util runnable in both main and renderer process */
const { dialog, app } = remote;
let instance = null;
class LinuxUpdateUtil {
constructor() {
if (instance) {
return instance;
} else {
instance = this;
}
this.reloadDB();
return instance;
}
getUpdateItem(key, defaultValue = null) {
this.reloadDB();
const value = this.db.getData('/')[key];
if (value === undefined) {
this.setUpdateItem(key, defaultValue);
return defaultValue;
} else {
return value;
}
}
setUpdateItem(key, value) {
this.db.push(`/${key}`, value, true);
this.reloadDB();
}
removeUpdateItem(key) {
this.db.delete(`/${key}`);
this.reloadDB();
}
reloadDB() {
const linuxUpdateJsonPath = path.join(app.getPath('userData'), '/updates.json');
try {
const file = fs.readFileSync(linuxUpdateJsonPath, 'utf8');
JSON.parse(file);
} catch (err) {
if (fs.existsSync(linuxUpdateJsonPath)) {
fs.unlinkSync(linuxUpdateJsonPath);
dialog.showErrorBox(
'Error saving update notifications.',
'We encountered error while saving update notifications.'
);
logger.error('Error while JSON parsing updates.json: ');
logger.error(err);
}
}
this.db = new JsonDB(linuxUpdateJsonPath, true, true);
}
}
module.exports = new LinuxUpdateUtil();

View File

@@ -1,87 +0,0 @@
const NodeConsole = require('console').Console;
const fs = require('fs');
const isDev = require('electron-is-dev');
const { initSetUp } = require('./default-util');
initSetUp();
let app = null;
if (process.type === 'renderer') {
app = require('electron').remote.app;
} else {
app = require('electron').app;
}
const browserConsole = console;
const logDir = `${app.getPath('userData')}/Logs`;
class Logger {
constructor(opts = {}) {
let {
timestamp = true,
file = 'console.log',
level = true,
logInDevMode = false
} = opts;
file = `${logDir}/${file}`;
if (timestamp === true) {
timestamp = this.getTimestamp;
}
const fileStream = fs.createWriteStream(file, { flags: 'a' });
const nodeConsole = new NodeConsole(fileStream);
this.nodeConsole = nodeConsole;
this.timestamp = timestamp;
this.level = level;
this.logInDevMode = logInDevMode;
this.setUpConsole();
}
_log(type, ...args) {
const {
nodeConsole, timestamp, level, logInDevMode
} = this;
let nodeConsoleLog;
/* eslint-disable no-fallthrough */
switch (true) {
case typeof timestamp === 'function':
args.unshift(timestamp() + ' |\t');
case (level !== false):
args.unshift(type.toUpperCase() + ' |');
case isDev || logInDevMode:
nodeConsoleLog = nodeConsole[type] || nodeConsole.log;
nodeConsoleLog.apply(null, args);
default: break;
}
/* eslint-enable no-fallthrough */
browserConsole[type].apply(null, args);
}
setUpConsole() {
for (const type in browserConsole) {
this.setupConsoleMethod(type);
}
}
setupConsoleMethod(type) {
this[type] = (...args) => {
this._log(type, ...args);
};
}
getTimestamp() {
const date = new Date();
const timestamp =
`${date.getMonth()}/${date.getDate()} ` +
`${date.getMinutes()}:${date.getSeconds()}`;
return timestamp;
}
}
module.exports = Logger;

View File

@@ -1,52 +0,0 @@
const isOnline = require('is-online');
class ReconnectUtil {
constructor(serverManagerView) {
this.serverManagerView = serverManagerView;
this.alreadyReloaded = false;
}
clearState() {
this.alreadyReloaded = false;
}
pollInternetAndReload() {
const pollInterval = setInterval(() => {
this._checkAndReload()
.then(status => {
if (status) {
this.alreadyReloaded = true;
clearInterval(pollInterval);
}
});
}, 1500);
}
_checkAndReload() {
return new Promise(resolve => {
if (!this.alreadyReloaded) { // eslint-disable-line no-negated-condition
isOnline()
.then(online => {
if (online) {
if (!this.alreadyReloaded) {
this.serverManagerView.reloadView();
}
console.log('You\'re back online.');
return resolve(true);
}
console.log('There is no internet connection, try checking network cables, modem and router.');
const errMsgHolder = document.querySelector('#description');
errMsgHolder.innerHTML = `
<div>You internet connection does't seem to work properly!</div>
</div>Verify that it works and then click try again.</div>`;
return resolve(false);
});
} else {
return resolve(true);
}
});
}
}
module.exports = ReconnectUtil;

View File

@@ -28,10 +28,6 @@
<i class="material-icons md-48">refresh</i>
<span id="reload-tooltip" style="display:none">Reload</span>
</div>
<div class="action-button disable" id="back-action">
<i class="material-icons md-48">arrow_back</i>
<span id="back-tooltip" style="display:none">Go Back</span>
</div>
<div class="action-button" id="settings-action">
<i class="material-icons md-48">settings</i>
<span id="setting-tooltip" style="display:none">Settings</span>
@@ -44,4 +40,5 @@
</div>
</body>
<script src="js/main.js"></script>
</html>

View File

@@ -6,7 +6,7 @@ os: Previous Visual Studio 2015
cache:
- node_modules
install:
- ps: Install-Product node 6 x64
- git reset --hard HEAD
@@ -20,5 +20,4 @@ install:
build: off
test_script:
- npm run test
- npm run test-e2e
- npm run test-all

View File

@@ -14,7 +14,7 @@ gulp.task('dev', () => {
// Reload renderer process
gulp.watch('app/renderer/css/*.css', ['reload:renderer']);
gulp.watch('app/renderer/*.html', ['reload:renderer']);
gulp.watch('app/renderer/js/**/*.js', ['reload:renderer']);
gulp.watch('app/renderer/js/*.js', ['reload:renderer']);
});
gulp.task('restart:browser', done => {
@@ -30,7 +30,7 @@ gulp.task('reload:renderer', done => {
});
gulp.task('test-e2e', () => {
return gulp.src('tests/*.js')
return gulp.src('tests/e2e/*.js')
.pipe(tape({
reporter: tapColorize()
}));

21
karma.conf.js Normal file
View File

@@ -0,0 +1,21 @@
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine'],
browsers: ['Electron'],
preprocessors: {
'**/*.js': ['electron']
},
files: [
{pattern: './karma.shim.js', watched: true, included: true, served: true},
{pattern: './tests/unit/*.js', watched: true, included: true, served: true},
{pattern: './app/renderer/**/*.js', watched: true, included: false, served: true}
],
reporters: ['mocha'],
client: {
captureConsole: true,
useIframe: false
},
singleRun: true
});
};

5
karma.shim.js Normal file
View File

@@ -0,0 +1,5 @@
window.require = window.parent.require;
window.process = window.parent.process;
window.__dirname = window.parent.__dirname;
require('module').globalPaths.push('./node_modules');
require('module').globalPaths.push('./app/renderer');

3586
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "zulip",
"productName": "Zulip",
"version": "1.8.2",
"version": "1.7.0",
"main": "./app/main",
"description": "Zulip Desktop App",
"license": "Apache-2.0",
@@ -17,23 +17,22 @@
"bugs": {
"url": "https://github.com/zulip/zulip-electron/issues"
},
"engines": {
"node": ">=8.0.0"
},
"scripts": {
"start": "electron app --disable-http-cache --no-electron-connect",
"reinstall": "./tools/reinstall-node-modules",
"start": "electron app --disable-http-cache",
"reinstall": "rm -rf node_modules; rm -rf app/node_modules; npm install",
"postinstall": "electron-builder install-app-deps",
"test": "xo",
"lint": "xo",
"test-e2e": "gulp test-e2e",
"dev": "gulp dev & nodemon --watch app/main --watch app/renderer --exec 'npm test' -e html,css,js",
"test-unit": "karma start karma.conf.js",
"test-all": "xo && npm run test-unit && npm run test-e2e",
"dev": "gulp dev",
"pack": "electron-builder --dir",
"dist": "electron-builder",
"mas": "electron-builder --mac mas",
"travis": "cd ./scripts && ./travis-build-test.sh"
},
"pre-commit": [
"test"
"lint"
],
"build": {
"appId": "org.zulip.zulip-electron",
@@ -112,18 +111,24 @@
"assert": "1.4.1",
"cp-file": "^5.0.0",
"devtron": "1.4.0",
"electron": "1.8.2",
"electron-builder": "19.53.6",
"electron-builder": "19.46.4",
"electron": "1.7.9",
"electron-connect": "0.6.2",
"electron-debug": "1.4.0",
"gulp": "3.9.1",
"gulp-tape": "0.0.9",
"is-ci": "^1.0.10",
"nodemon": "^1.14.11",
"jasmine": "^2.8.0",
"karma": "^1.7.1",
"karma-electron": "^5.2.2",
"karma-electron-launcher": "^0.2.0",
"karma-jasmine": "^1.1.0",
"karma-mocha-reporter": "^2.2.5",
"pre-commit": "1.2.2",
"spectron": "3.7.2",
"tap-colorize": "^1.2.0",
"tape": "^4.8.0",
"watchify": "^3.9.0",
"xo": "0.18.2"
},
"xo": {
@@ -140,11 +145,7 @@
"rules": {
"max-lines": [
"warn",
{
"max": 500,
"skipBlankLines": true,
"skipComments": true
}
500
],
"no-warning-comments": 0,
"object-curly-spacing": 0,
@@ -161,7 +162,8 @@
}
],
"ignore": [
"tests/*.js"
"tests/e2e/*.js",
"tests/unit/*"
],
"envs": [
"node",

View File

@@ -1,8 +1,5 @@
#!/usr/bin/env bash
# exit script if fails
set -e;
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
export {no_proxy,NO_PROXY}="127.0.0.1,localhost"
export DISPLAY=:99.0
@@ -13,8 +10,14 @@ if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
xdpyinfo | grep dimensions
fi
npm run test
# macOS
# Run all the tests
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
npm run test-e2e
npm run test-all
fi
# Linux
# Only run linting test on Linux
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
npm run lint
fi

View File

@@ -22,7 +22,7 @@ module.exports = {
function createApp (t) {
generateTestAppPackageJson()
return new Application({
path: path.join(__dirname, '..', 'node_modules', '.bin',
path: path.join(__dirname,'..', '..', 'node_modules', '.bin',
'electron' + (process.platform === 'win32' ? '.cmd' : '')),
args: [path.join(__dirname)], // Ensure this dir has a package.json file with a 'main' entry piont
env: {NODE_ENV: 'test'},
@@ -34,9 +34,9 @@ function createApp (t) {
// Reads app package.json and updates the productName to config.TEST_APP_PRODUCT_NAME
// We do this so that the app integration doesn't doesn't share the same appDataDir as the dev application
function generateTestAppPackageJson () {
let packageJson = require(path.join(__dirname, '../package.json'))
let packageJson = require(path.join(__dirname, '..', '..', 'package.json'))
packageJson.productName = config.TEST_APP_PRODUCT_NAME
packageJson.main = '../app/main'
packageJson.main = '../../app/main'
const testPackageJsonPath = path.join(__dirname, 'package.json')
fs.writeFileSync(testPackageJsonPath, JSON.stringify(packageJson, null, ' '), 'utf-8')

View File

@@ -0,0 +1,20 @@
const ConfigUtil = require('js/utils/config-util.js');
const SetupSpellChecker = require('js/spellchecker')
describe('test spell checker', function () {
// enable spellchecker settings
ConfigUtil.setConfigItem('enableSpellchecker', true);
ConfigUtil.setConfigItem('spellcheckerLanguage', 'en');
SetupSpellChecker.init() // re-initialize after setting update
const spellCheckHandler = SetupSpellChecker.SpellCheckHandler
it('mark misspelled word', function () {
expect(spellCheckHandler.isMisspelled('helpe')).toBe(true)
})
it('verify properly spelled word', function () {
expect(spellCheckHandler.isMisspelled('help')).toBe(false)
})
})

View File

@@ -1,16 +0,0 @@
#!/bin/bash
set -e
set -x
if ! git diff-index --quiet HEAD; then
set +x
echo "There are uncommitted changes:"
git status --short
echo "Doing nothing to avoid losing your work."
exit 1
fi
request_id="$1"
remote=${2:-"upstream"}
git fetch "$remote" "pull/$request_id/head"
git checkout -B "review-original-${request_id}"
git reset --hard FETCH_HEAD

View File

@@ -1,23 +0,0 @@
@echo off
git diff-index --quiet HEAD
if %ERRORLEVEL% NEQ 0 (
echo "There are uncommitted changes:"
git status --short
echo "Doing nothing to avoid losing your work."
exit /B 1
)
if "%~1"=="" (
echo "Error you must specify the PR number"
)
if "%~2"=="" (
set remote="upstream"
) else (
set remote=%2
)
set request_id="%1"
git fetch "%remote%" "pull/%request_id%/head"
git checkout -B "review-%request_id%"
git reset --hard FETCH_HEAD

View File

@@ -1,17 +0,0 @@
#!/bin/bash
set -e
set -x
if ! git diff-index --quiet HEAD; then
set +x
echo "There are uncommitted changes:"
git status --short
echo "Doing nothing to avoid losing your work."
exit 1
fi
request_id="$1"
remote=${2:-"upstream"}
git fetch "$remote" "pull/$request_id/head"
git checkout -B "review-${request_id}" $remote/master
git reset --hard FETCH_HEAD
git pull --rebase

View File

@@ -1,24 +0,0 @@
@echo off
git diff-index --quiet HEAD
if %errorlevel% neq 0 (
echo "There are uncommitted changes:"
git status --short
echo "Doing nothing to avoid losing your work."
exit \B 1
)
if "%~1"=="" (
echo "Error you must specify the PR number"
)
if "%~2"=="" (
set remote="upstream"
) else (
set remote=%2
)
set request_id="%1"
git fetch "%remote%" "pull/%request_id%/head"
git checkout -B "review-%request_id%" %remote%/master
git reset --hard FETCH_HEAD
git pull --rebase

View File

@@ -1,10 +0,0 @@
#!/bin/bash
set -e
set -x
echo "Removing node_modules and app/node_modules"
rm -rf node_modules
rm -rf app/node_modules
echo "node_modules removed reinstalling npm packages"
npm i

View File

@@ -1,8 +0,0 @@
@echo off
echo "Removing node_modules and app/node_modules"
rmdir /s /q node_modules
rmdir /s /q app/node_modules
echo "node_modules removed reinstalling npm packages"
npm i