Compare commits

..

89 Commits

Author SHA1 Message Date
akashnimare
a0fc92df3e Add tray icon from favicons. 2018-01-15 17:04:04 +05:30
akashnimare
8d9aa2fb58 Add tray icon from favicons. 2018-01-15 17:03:27 +05:30
Priyank P
91742a5770 silent: Reflect changes in webview for silent option. (#380)
* silent: Reflect changes in webview for mute/silent option.

This silent the webview incase silent option is toggled, and
by default silent the webview when its create if needed.

Fixes: #380.
2018-01-11 18:46:39 +05:30
akashnimare
fb74251a2c performance: Disable hardware acceleration to decrease the load on GPU.
Adding this experimental electron api to see if it makes any difference
in performance.

Improves #213.
2018-01-08 02:10:43 +05:30
akashnimare
a920720f91 setting-page: Improve add new server page #340. 2018-01-07 15:10:21 +05:30
Priyank P
aa8e99b7a6 domain-util: Fix checkDomain, so it checks all error codes. (#370)
This fixes an issue where if server send non 404 error code such
as 403 forbidden we marked them as Zulip server even though they are
not, now it checks for 400 error range.
2018-01-07 00:16:52 +05:30
akashnimare
e23f8aaa58 shortcut: Rename Zoom In keyboard shortcut. 2018-01-06 16:30:59 +05:30
Akash Nimare
5c3208d44c settings: Add a setting option to start the app in background. (#366)
Fixes #314.
2017-12-29 12:37:53 +05:30
akashnimare
c0b57bbe2b settings: Set default value of flashtaskbar setting[Windows]. 2017-12-28 00:29:16 +05:30
Priyank P
afe4e8901b github templates: Add pull request template and update issue template. (#365) 2017-12-27 23:15:02 +05:30
Priyank P
231e7fd9c2 preferences page: Reflect changes in the preference page. (#362)
This updated the setting page if the sidebar was toggled using a shortcut.
This also updates the setting page if the tray was toggled using menu.

Fixes: #304.
2017-12-27 21:06:32 +05:30
Akash Nimare
a0d898a5b7 Merge pull request #363 from cPhost/logger
logger-util: Code refactoring and better logs design.
2017-12-27 20:45:13 +05:30
cPhost
1abf62555c logger-util: code refactoring and better logs design.
This imporves logging and refactors most of the code.
This also renames console-util to logger-util.
2017-12-26 22:03:05 -05:00
Akash Nimare
6befcbaa8f Merge pull request #361 from cPhost/focus-app
notifications: Focus app when a notification is triggered.
2017-12-27 01:08:32 +05:30
cPhost
e56a01049b notifications: Focus app when a notification is triggered.
This PR adds a feature of showing app window when the notification are clicked
Fixes: #358
2017-12-26 14:30:33 -05:00
akashnimare
72cb8459ff code refactoring. 2017-12-26 01:02:42 +05:30
Akash Nimare
0b83b22206 Merge pull request #353 from cPhost/fix-errors
default-util: Fix log dir errors.
2017-12-26 00:21:35 +05:30
Akash Nimare
267d25e5c4 Merge pull request #357 from cPhost/img-fix
Add default icon if the server image is not available.
2017-12-25 23:43:35 +05:30
cPhost
8401f8f5ce server icon: Load default icon if org icon is not avalible. 2017-12-25 12:17:45 -05:00
cPhost
c4a7264f34 console: Fix errors where Logs dir can't be created. 2017-12-25 09:50:52 -05:00
akashnimare
9d081ecd5a Reload full app on system hibernation.
Improves #312.
2017-12-20 02:33:08 +05:30
akashnimare
dc6582fa82 Logs: Unused Logs dir. 2017-12-19 03:42:42 +05:30
akashnimare
3b412672c6 Logs: Fix an issue where Logs dir don't get init properly. 2017-12-19 03:40:24 +05:30
Akash Nimare
04083bfa81 Merge pull request #352 from zulip/cPhost-handle-domainjson
Handle corrupted config files.
Improves #310.
2017-12-19 02:33:39 +05:30
akashnimare
562e82d2f1 test: Create logs dir on app startup. 2017-12-19 01:22:50 +05:30
cPhost
3b014e0715 settings util: delete settings.json file in case it is corrupted. 2017-12-17 15:17:08 -05:00
cPhost
13178ebc8f domain util: delete domain.json file in case it is corrupted. 2017-12-17 15:16:55 -05:00
cPhost
08693bf105 console: require app form remote if required 2017-12-17 15:00:04 -05:00
akashnimare
d7a0b63d62 test: fix failing e2e test on travis 2017-12-13 20:05:07 +05:30
akashnimare
a193ecf229 design: update new server page #340. 2017-12-13 18:54:31 +05:30
akashnimare
31f04754a4 design: fixed positioning of create-new-org link 2017-12-11 16:08:23 +05:30
akashnimare
ae7374475f design: Update UI for server section #340 2017-12-11 15:42:50 +05:30
akashnimare
7697d5d698 electron: update electron to v1.7.9 #213 2017-12-11 15:09:56 +05:30
akashnimare
d3e1b5de45 update app configs 2017-12-06 19:46:24 +05:30
Akash Nimare
9efa6191f7 Merge pull request #349 from zulip/improve-debian-installer
linux: Improve debian installer
2017-12-06 18:17:57 +05:30
akashnimare
d86797d2fc debian: replace bash shebang with '#!/usr/bin/env bash" for portability.
According to the debian config rules '#!/usr/bin/env bash' violates
the quality standard which isn't allowed.

More info - https://github.com/Microsoft/vscode/issues/35638
2017-12-05 23:58:26 +05:30
akashnimare
23eef7edb0 debian: remove app settings on uninstall 2017-12-05 22:15:18 +05:30
akashnimare
a1d5a35ccf debian: Remove config files when user uninstalls app 2017-12-05 21:01:26 +05:30
akashnimare
62e8dfe180 Rename afterRemove script to debian uninstaller 2017-12-05 20:54:39 +05:30
Akash Nimare
8322054984 Merge pull request #347 from cPhost/deb
debian-installer: add apt repo for debian
2017-12-05 18:42:15 +05:30
Akash Nimare
13ae6f07e9 Add comment for adding apt repo 2017-12-05 18:41:06 +05:30
cPhost
dab92be54c debian installer: remove the apt repo after unistallation 2017-12-05 07:29:45 -05:00
cPhost
fe9c66d8c2 debian-installer: add apt repo for debian 2017-12-05 07:29:34 -05:00
akashnimare
43b4d511dc init default settings properly fixes #348 2017-12-04 17:19:25 +05:30
akashnimare
aa5a47ad53 Make app window thinner fixes #332 2017-12-04 15:19:43 +05:30
Akash Nimare
ce27f92900 Merge pull request #346 from cPhost/about-page
about-page: add links for zulip-electron and license.
2017-12-04 14:54:13 +05:30
cPhost
7be051bb6e about-page: center everything, add links for zulip-electron, license 2017-12-03 18:02:34 -05:00
Akash Nimare
c2a01adabe Merge pull request #345 from cPhost/logger
logger: add console helper to log for both file and console
2017-12-04 01:13:07 +05:30
cPhost
770926e6eb logger: add console helper to log for both file and console 2017-12-02 11:58:28 -05:00
Akash Nimare
10ef627f59 Add notifications troubleshooting 2017-12-01 20:43:52 +05:30
akashnimare
4adba0f4b4 Update electron-builder to v19.46.4 2017-11-24 17:00:03 +05:30
akashnimare
ed590c26e3 Added whitelist zulip server 2017-11-23 22:38:28 +05:30
akashnimare
6d10291a87 🎉 v1.7.0 2017-11-22 20:10:59 +05:30
akashnimare
3fe3a3da85 Update electron to v1.6.15 2017-11-22 16:21:39 +05:30
akashnimare
ba64438a99 Improve Add Server page for new users [WIP] #340 2017-11-22 14:39:39 +05:30
Akash Nimare
bcc27894c4 Merge pull request #337 from SimplyAhmazing/setup-e2e-tests
setup e2e tests
2017-11-22 13:28:31 +05:30
Akash Nimare
0dd0f593d1 Merge pull request #338 from sroy8091/issue_#336
Better error message on invalid server fixes #336
2017-11-21 21:08:08 +05:30
Sumit Roy
f5e9342f78 fail to connect message #336 2017-11-21 11:31:42 +05:30
simplyahmazing
083ccdf229 remove unused imports 2017-11-20 11:14:22 -05:00
simplyahmazing
1261786db2 remove unused dependencies 2017-11-20 11:06:05 -05:00
simplyahmazing
7f567f55c3 setup e2e tests 2017-11-19 16:48:21 -05:00
akashnimare
dcd2abca6e v1.6.0-beta 2017-11-16 00:32:32 +05:30
Akash Nimare
2fb9efb981 Merge pull request #335 from zulip/electron-shortcut-fix
Remove electron-localshortcut completely
2017-11-15 16:56:42 +05:30
akashnimare
7245b6a110 Move electron-debug to devDependencies 2017-11-15 04:59:59 +05:30
akashnimare
bcb8ffb55f Remove electron-LocalShortcut completely
This commit removes the usage of "electron-localshortcut" completely.
Now, we rely on only menu accelerators for shortcuts. It's risky to register a local shortcuts in the app window
either using electronLocalShortcut or globalShortcut as the registered shortcuts could interfare with
OS global shortcuts which is very frustrating. This fixes #317 once and for all.
2017-11-15 03:39:51 +05:30
akashnimare
77094596a5 Use electron-debug in development only WIP #317
electron-debug hijacks the CMD/CTRL+R and reloads the whole app,
whereas we only need to reload the current server. Removed those commands from
electronLocalShortcuts as well as they are already registered in menu items.
2017-11-15 03:07:36 +05:30
Akash Nimare
06ad44bdd7 Merge pull request #333 from zulip/appimage-autoupdates
Add AppImage autoupdates fixes #333
2017-11-11 20:12:24 +05:30
akashnimare
e719ba139c Update electron-builder to latest version 2017-11-11 20:10:53 +05:30
akashnimare
9853e9226c Better tray icon for retina display, fixes #330 2017-11-08 12:26:52 +05:30
akashnimare
f2c76b5ca3 Enable auto-update on Linux (AppImage) 2017-11-04 01:31:45 +05:30
Akash Nimare
e6dbff995b Merge pull request #327 from zulip/taskbar-setting-option
Setting option for Windows taskbar flash fixes #299
2017-11-03 00:09:58 +05:30
akashnimare
4578d4a5f7 typo in setting 2017-11-03 00:06:34 +05:30
akashnimare
4b895a2312 Don't show flash taskbar setting on Linux/macOS 2017-11-02 23:54:38 +05:30
akashnimare
53c0428a3a Add setting to control Windows taskbar flashing #299 2017-11-02 20:58:35 +05:30
akashnimare
0a1866abb5 Show Detailed error message on invaild Zulip server #325 2017-11-01 17:59:00 +05:30
Akash Nimare
ce862a4890 Merge pull request #326 from zulip/remove-python-version-file
Remove python version file

Fixes failing tests on Travis Linux
2017-10-31 20:55:17 +05:30
simplyahmazing
1b1ad2cd61 remove .python-version file from repo 2017-10-28 20:06:56 -04:00
simplyahmazing
ead7a06308 ignore .python-version files 2017-10-28 20:06:26 -04:00
akashnimare
6659dd5097 Update electron-builder & updater to latest 2017-10-26 11:54:55 +05:30
akashnimare
ed1f0f6d5b Update electron-builder & updater to latest 2017-10-24 18:38:00 +05:30
akashnimare
79acf8a6e1 Add option to remove app settings
Menu item "Reset app settings" now remove all the configurations/settings files related to app.
Previously it used to remove only window-state.json. This helps a bit in #310.
2017-10-24 18:29:35 +05:30
akashnimare
8e0033f03e Handle certificate issuer error
Fixes, #316
2017-10-21 01:47:16 +05:30
Akash Nimare
9144c2630d Merge pull request #322 from zulip/spellchecker-osx-fix
Spellchecker Improvements
2017-10-20 23:52:24 +05:30
akashnimare
fae05fc3b1 Initialize default app settings
Settings are initialized only when user clicks on General/Server/Network section settings
In case, user doesn't visit these section, those values set to be null automatically.
This fix makes sure the default settings are correctly set to either true or false.
2017-10-18 21:38:48 +05:30
akashnimare
73603a4fd2 Add settings to disable/enable spellchecker 2017-10-18 21:36:01 +05:30
akashnimare
a498ffc7d6 Update spellchecker to v1.1.2 2017-10-18 04:17:51 +05:30
akashnimare
7afcf13401 Re-write and improve spellchecker class
Rewrote the Spellchecker class so that we can have better control
over the context menu and spellchecker.
2017-10-18 04:14:02 +05:30
akashnimare
89a292559d Set English as default language for spellchecker on Linux/Windows 2017-10-17 21:57:51 +05:30
akashnimare
be14517caf Set server language for spellchecker on macOS
Ideally spellchecker should detect the language, but on macOS, it fails to auto-detect the lanugage user is typing in
that's why we need to mention it explicitly. We set this language with the help of the default language of the server.
2017-10-17 00:29:50 +05:30
154 changed files with 9424 additions and 446 deletions

8
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

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

16
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,16 @@
---
<!--
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

7
.gitignore vendored
View File

@@ -23,4 +23,9 @@ yarn-error.log*
# miscellaneous # miscellaneous
.idea .idea
config.gypi config.gypi
# Test generated files
tests/package.json
.python-version

View File

@@ -1 +0,0 @@
2.7.9

View File

@@ -18,8 +18,9 @@ node_js:
- '6' - '6'
before_install: before_install:
- npm install -g gulp - ./scripts/travis-xvfb.sh
- npm install - npm install -g gulp
- npm install
cache: cache:
directories: directories:
@@ -33,4 +34,4 @@ notifications:
urls: urls:
- https://zulip.org/zulipbot/travis - https://zulip.org/zulipbot/travis
on_success: always on_success: always
on_failure: always on_failure: always

View File

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

View File

@@ -1,5 +1,4 @@
'use strict'; 'use strict';
const fs = require('fs');
const { app, dialog } = require('electron'); const { app, dialog } = require('electron');
const { autoUpdater } = require('electron-updater'); const { autoUpdater } = require('electron-updater');
const isDev = require('electron-is-dev'); const isDev = require('electron-is-dev');
@@ -7,19 +6,14 @@ const isDev = require('electron-is-dev');
const ConfigUtil = require('./../renderer/js/utils/config-util.js'); const ConfigUtil = require('./../renderer/js/utils/config-util.js');
function appUpdater() { function appUpdater() {
// Don't initiate auto-updates in development and on Linux system // Don't initiate auto-updates in development
// since autoUpdater doesn't work on Linux if (isDev) {
if (isDev || process.platform === 'linux') {
return; return;
} }
// Create Logs directory // Create Logs directory
const LogsDir = `${app.getPath('userData')}/Logs`; const LogsDir = `${app.getPath('userData')}/Logs`;
if (!fs.existsSync(LogsDir)) {
fs.mkdirSync(LogsDir);
}
// Log whats happening // Log whats happening
const log = require('electron-log'); const log = require('electron-log');

View File

@@ -1,8 +1,8 @@
'use strict'; 'use strict';
const path = require('path'); const path = require('path');
const electron = require('electron'); const electron = require('electron');
const electronLocalshortcut = require('electron-localshortcut');
const windowStateKeeper = require('electron-window-state'); const windowStateKeeper = require('electron-window-state');
const isDev = require('electron-is-dev');
const appMenu = require('./menu'); const appMenu = require('./menu');
const { appUpdater } = require('./autoupdater'); const { appUpdater } = require('./autoupdater');
const { crashHandler } = require('./crash-reporter'); const { crashHandler } = require('./crash-reporter');
@@ -12,9 +12,13 @@ const { setAutoLaunch } = require('./startup');
const { app, ipcMain } = electron; const { app, ipcMain } = electron;
const BadgeSettings = require('./../renderer/js/pages/preference/badge-settings.js'); 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 // Adds debug features like hotkeys for triggering dev tools and reload
require('electron-debug')(); // in development mode
if (isDev) {
require('electron-debug')();
}
// Prevent window being garbage collected // Prevent window being garbage collected
let mainWindow; let mainWindow;
@@ -63,8 +67,8 @@ function createMainWindow() {
y: mainWindowState.y, y: mainWindowState.y,
width: mainWindowState.width, width: mainWindowState.width,
height: mainWindowState.height, height: mainWindowState.height,
minWidth: 600, minWidth: 300,
minHeight: 500, minHeight: 400,
webPreferences: { webPreferences: {
plugins: true, plugins: true,
allowDisplayingInsecureContent: true, allowDisplayingInsecureContent: true,
@@ -78,7 +82,11 @@ function createMainWindow() {
}); });
win.once('ready-to-show', () => { win.once('ready-to-show', () => {
win.show(); if (ConfigUtil.getConfigItem('startMinimized')) {
win.minimize();
} else {
win.show();
}
}); });
win.loadURL(mainURL); win.loadURL(mainURL);
@@ -94,9 +102,6 @@ function createMainWindow() {
win.hide(); win.hide();
} }
} }
// Unregister all the shortcuts so that they don't interfare with other apps
electronLocalshortcut.unregisterAll(mainWindow);
}); });
win.setTitle('Zulip'); win.setTitle('Zulip');
@@ -124,21 +129,8 @@ function createMainWindow() {
return win; return win;
} }
function registerLocalShortcuts(page) { // Decrease load on GPU (experimental)
// Somehow, reload action cannot be overwritten by the menu item app.disableHardwareAcceleration();
electronLocalshortcut.register(mainWindow, 'CommandOrControl+R', () => {
page.send('reload-current-viewer');
});
// Also adding these shortcuts because some users might want to use it instead of CMD/Left-Right
electronLocalshortcut.register(mainWindow, 'CommandOrControl+[', () => {
page.send('back');
});
electronLocalshortcut.register(mainWindow, 'CommandOrControl+]', () => {
page.send('forward');
});
}
// eslint-disable-next-line max-params // eslint-disable-next-line max-params
app.on('certificate-error', (event, webContents, url, error, certificate, callback) => { app.on('certificate-error', (event, webContents, url, error, certificate, callback) => {
@@ -146,11 +138,6 @@ app.on('certificate-error', (event, webContents, url, error, certificate, callba
callback(true); callback(true);
}); });
app.on('window-all-closed', () => {
// Unregister all the shortcuts so that they don't interfare with other apps
electronLocalshortcut.unregisterAll(mainWindow);
});
app.on('activate', () => { app.on('activate', () => {
if (!mainWindow) { if (!mainWindow) {
mainWindow = createMainWindow(); mainWindow = createMainWindow();
@@ -165,10 +152,12 @@ app.on('ready', () => {
const page = mainWindow.webContents; const page = mainWindow.webContents;
registerLocalShortcuts(page);
page.on('dom-ready', () => { page.on('dom-ready', () => {
mainWindow.show(); if (ConfigUtil.getConfigItem('startMinimized')) {
mainWindow.minimize();
} else {
mainWindow.show();
}
}); });
page.once('did-frame-finish-load', () => { page.once('did-frame-finish-load', () => {
@@ -178,7 +167,8 @@ app.on('ready', () => {
}); });
electron.powerMonitor.on('resume', () => { electron.powerMonitor.on('resume', () => {
page.send('reload-viewer'); mainWindow.reload();
page.send('destroytray');
}); });
ipcMain.on('focus-app', () => { ipcMain.on('focus-app', () => {
@@ -232,18 +222,8 @@ app.on('ready', () => {
}); });
ipcMain.on('register-server-tab-shortcut', (event, index) => { ipcMain.on('register-server-tab-shortcut', (event, index) => {
electronLocalshortcut.register(mainWindow, `CommandOrControl+${index}`, () => { // Array index == Shown index - 1
// Array index == Shown index - 1 page.send('switch-server-tab', index - 1);
page.send('switch-server-tab', index - 1);
});
});
ipcMain.on('local-shortcuts', (event, enable) => {
if (enable) {
registerLocalShortcuts(page);
} else {
electronLocalshortcut.unregisterAll(mainWindow);
}
}); });
ipcMain.on('toggleAutoLauncher', (event, AutoLaunchValue) => { ipcMain.on('toggleAutoLauncher', (event, AutoLaunchValue) => {
@@ -251,11 +231,6 @@ app.on('ready', () => {
}); });
}); });
app.on('will-quit', () => {
// Unregister all the shortcuts so that they don't interfare with other apps
electronLocalshortcut.unregisterAll(mainWindow);
});
app.on('before-quit', () => { app.on('before-quit', () => {
isQuitting = true; isQuitting = true;
}); });

View File

@@ -54,7 +54,7 @@ class AppMenu {
role: 'togglefullscreen' role: 'togglefullscreen'
}, { }, {
label: 'Zoom In', label: 'Zoom In',
accelerator: 'CommandOrControl+=', accelerator: 'CommandOrControl+Plus',
click(item, focusedWindow) { click(item, focusedWindow) {
if (focusedWindow) { if (focusedWindow) {
AppMenu.sendAction('zoomIn'); AppMenu.sendAction('zoomIn');
@@ -370,10 +370,20 @@ class AppMenu {
} }
static resetAppSettings() { static resetAppSettings() {
const getAppPath = path.join(app.getPath('appData'), appName, 'window-state.json'); // We save App's settings/configurations in following files
const settingFiles = ['window-state.json', 'domain.json', 'settings.json'];
fs.unlink(getAppPath, () => { settingFiles.forEach(settingFileName => {
setTimeout(() => AppMenu.sendAction('clear-app-data'), 1000); const getSettingFilesPath = path.join(app.getPath('appData'), appName, settingFileName);
fs.access(getSettingFilesPath, error => {
if (error) {
console.log(error);
} else {
fs.unlink(getSettingFilesPath, () => {
AppMenu.sendAction('clear-app-data');
});
}
});
}); });
} }

904
app/package-lock.json generated Normal file
View File

@@ -0,0 +1,904 @@
{
"name": "zulip",
"version": "1.7.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@paulcbetts/cld": {
"version": "2.4.6",
"resolved": "https://registry.npmjs.org/@paulcbetts/cld/-/cld-2.4.6.tgz",
"integrity": "sha1-qZL2vEPKshKsLESIpnHPMC+LYuc=",
"requires": {
"glob": "5.0.15",
"nan": "2.8.0",
"rimraf": "2.6.2",
"underscore": "1.8.3"
}
},
"@paulcbetts/spellchecker": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/@paulcbetts/spellchecker/-/spellchecker-4.0.6.tgz",
"integrity": "sha512-9lhLEvWfAB00n2oOM/S08sna9AuFk+b+bPk8ficpSa2X0Ll40PahMwfFS3G54nqQBIFFZgTPrhoHtCLAao0xmg==",
"requires": {
"nan": "2.8.0"
}
},
"ajv": {
"version": "4.11.8",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
"integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
"requires": {
"co": "4.6.0",
"json-stable-stringify": "1.0.1"
}
},
"applescript": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/applescript/-/applescript-1.0.0.tgz",
"integrity": "sha1-u4evVoytA0pOSMS9r2Bno6JwExc="
},
"argparse": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
"integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
"requires": {
"sprintf-js": "1.0.3"
}
},
"asn1": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
"integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y="
},
"assert-plus": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
"integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ="
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"auto-launch": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/auto-launch/-/auto-launch-5.0.1.tgz",
"integrity": "sha1-IBWoowOEq+Dn+Yy9yoFFzxVHU64=",
"requires": {
"applescript": "1.0.0",
"mkdirp": "0.5.1",
"path-is-absolute": "1.0.1",
"untildify": "3.0.2",
"winreg": "1.2.2"
}
},
"aws-sign2": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
"integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8="
},
"aws4": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
"integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4="
},
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"bcp47": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/bcp47/-/bcp47-1.1.2.tgz",
"integrity": "sha1-NUvjMH/9CEM6ePXh4glYRfifx/4="
},
"bcrypt-pbkdf": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
"integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
"optional": true,
"requires": {
"tweetnacl": "0.14.5"
}
},
"bluebird": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
"integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
},
"bluebird-lst": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.5.tgz",
"integrity": "sha512-Ey0bDNys5qpYPhZ/oQ9vOEvD0TYQDTILMXWP2iGfvMg7rSDde+oV4aQQgqRH+CvBFNz2BSDQnPGMUl6LKBUUQA==",
"requires": {
"bluebird": "3.5.1"
}
},
"boom": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
"integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
"requires": {
"hoek": "2.16.3"
}
},
"brace-expansion": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
"integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
"requires": {
"balanced-match": "1.0.0",
"concat-map": "0.0.1"
}
},
"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": {
"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"
}
}
}
},
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
},
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
},
"combined-stream": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
"integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=",
"requires": {
"delayed-stream": "1.0.0"
}
},
"concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"cryptiles": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
"integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
"requires": {
"boom": "2.10.1"
}
},
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
"requires": {
"assert-plus": "1.0.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
}
}
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"deep-equal": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
"integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU="
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"ecc-jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
"integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
"optional": true,
"requires": {
"jsbn": "0.1.1"
}
},
"electron-is-dev": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/electron-is-dev/-/electron-is-dev-0.3.0.tgz",
"integrity": "sha1-FOb9pcaOnk7L7/nM8DfL18BcWv4="
},
"electron-log": {
"version": "2.2.7",
"resolved": "https://registry.npmjs.org/electron-log/-/electron-log-2.2.7.tgz",
"integrity": "sha512-pRfRn53MQGJ9L1+aC0VFcps0Uo5NM4RYsdvIdnjiV6J+krMr4cgBZ/DDA3kjNsr0D0kzo2WKpMgn2fAVra99rg=="
},
"electron-remote": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/electron-remote/-/electron-remote-1.2.0.tgz",
"integrity": "sha512-Fo2wnwK2tzd81Ux4pfNhz9DwHBoooZahdWBqOh9HtESYh2jrcsjc6FAibIu2oIOk71T1USyC7OBcqE8BZw3FGQ==",
"requires": {
"debug": "2.6.9",
"hashids": "1.1.4",
"lodash.get": "4.4.2",
"pify": "2.3.0",
"rxjs": "5.5.2",
"xmlhttprequest": "1.8.0"
}
},
"electron-spellchecker": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/electron-spellchecker/-/electron-spellchecker-1.1.2.tgz",
"integrity": "sha512-AdzD/Q82Svk9EDTc65vRr271UPLVIxsruKJM0iwqxEG9Y/CogNhEAJz/asV0BFWom4tpdB6cHcLbYePb11Musw==",
"requires": {
"@paulcbetts/cld": "2.4.6",
"@paulcbetts/spellchecker": "4.0.6",
"bcp47": "1.1.2",
"debug": "2.6.9",
"electron-remote": "1.2.0",
"keyboard-layout": "2.0.13",
"lru-cache": "4.1.1",
"mkdirp": "0.5.1",
"pify": "2.3.0",
"rxjs": "5.5.2",
"rxjs-serial-subscription": "0.1.1",
"spawn-rx": "2.0.12"
}
},
"electron-updater": {
"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": "3.2.0",
"electron-is-dev": "0.3.0",
"fs-extra-p": "4.4.4",
"js-yaml": "3.10.0",
"lazy-val": "1.0.2",
"lodash.isequal": "4.5.0",
"semver": "5.4.1",
"source-map-support": "0.5.0"
}
},
"electron-window-state": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/electron-window-state/-/electron-window-state-4.1.1.tgz",
"integrity": "sha1-azT9wxs4UU3+yLfI97XUrdtnYy0=",
"requires": {
"deep-equal": "1.0.1",
"jsonfile": "2.4.0",
"mkdirp": "0.5.1"
},
"dependencies": {
"jsonfile": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
"integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
"requires": {
"graceful-fs": "4.1.11"
}
}
}
},
"esprima": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
"integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw=="
},
"event-kit": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/event-kit/-/event-kit-2.4.0.tgz",
"integrity": "sha512-ZXd9jxUoc/f/zdLdR3OUcCzT84WnpaNWefquLyE125akIC90sDs8S3T/qihliuVsaj7Osc0z8lLL2fjooE9Z4A=="
},
"extend": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
"integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
},
"extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
},
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
},
"form-data": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz",
"integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=",
"requires": {
"asynckit": "0.4.0",
"combined-stream": "1.0.5",
"mime-types": "2.1.17"
}
},
"fs-extra": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.2.tgz",
"integrity": "sha1-+RcExT0bRh+JNFKwwwfZmXZHq2s=",
"requires": {
"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": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
"requires": {
"assert-plus": "1.0.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
}
}
},
"glob": {
"version": "5.0.15",
"resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
"integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
"requires": {
"inflight": "1.0.6",
"inherits": "2.0.3",
"minimatch": "3.0.4",
"once": "1.4.0",
"path-is-absolute": "1.0.1"
}
},
"graceful-fs": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
},
"har-schema": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz",
"integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4="
},
"har-validator": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz",
"integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=",
"requires": {
"ajv": "4.11.8",
"har-schema": "1.0.5"
}
},
"hashids": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/hashids/-/hashids-1.1.4.tgz",
"integrity": "sha512-U/fnTE3edW0AV92ZI/BfEluMZuVcu3MDOopsN7jS+HqDYcarQo8rXQiWlsBlm0uX48/taYSdxRsfzh2HRg5Z6w=="
},
"hawk": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
"integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=",
"requires": {
"boom": "2.10.1",
"cryptiles": "2.0.5",
"hoek": "2.16.3",
"sntp": "1.0.9"
}
},
"hoek": {
"version": "2.16.3",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
"integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0="
},
"http-signature": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
"integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=",
"requires": {
"assert-plus": "0.2.0",
"jsprim": "1.4.1",
"sshpk": "1.13.1"
}
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"requires": {
"once": "1.4.0",
"wrappy": "1.0.2"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
"js-yaml": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz",
"integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==",
"requires": {
"argparse": "1.0.9",
"esprima": "4.0.0"
}
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
"optional": true
},
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
},
"json-stable-stringify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
"integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
"requires": {
"jsonify": "0.0.0"
}
},
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
},
"jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
"requires": {
"graceful-fs": "4.1.11"
}
},
"jsonify": {
"version": "0.0.0",
"resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
"integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM="
},
"jsprim": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
"json-schema": "0.2.3",
"verror": "1.10.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
}
}
},
"keyboard-layout": {
"version": "2.0.13",
"resolved": "https://registry.npmjs.org/keyboard-layout/-/keyboard-layout-2.0.13.tgz",
"integrity": "sha512-WxVc3bBITttHozSyEYPsyr5rN2KQuXtEaXMlQfQjEze1JrkLw30yH/bcNn1IGx48b+tdOdybpnq++JFLU2FaZg==",
"requires": {
"event-kit": "2.4.0",
"nan": "2.8.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",
"resolved": "https://registry.npmjs.org/lodash.assign/-/lodash.assign-4.2.0.tgz",
"integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc="
},
"lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
},
"lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
},
"lru-cache": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.1.tgz",
"integrity": "sha512-q4spe4KTfsAS1SUHLO0wz8Qiyf1+vMIAgpRYioFYDMNqKfHQbg+AVDH3i4fvpl71/P1L0dBl+fQi+P37UYf0ew==",
"requires": {
"pseudomap": "1.0.2",
"yallist": "2.1.2"
}
},
"mime-db": {
"version": "1.30.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz",
"integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE="
},
"mime-types": {
"version": "2.1.17",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz",
"integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=",
"requires": {
"mime-db": "1.30.0"
}
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"requires": {
"brace-expansion": "1.1.8"
}
},
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"nan": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz",
"integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo="
},
"node-json-db": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/node-json-db/-/node-json-db-0.7.3.tgz",
"integrity": "sha1-v2Mf9NTPQhHL3/5srmqq/m7lTN8=",
"requires": {
"mkdirp": "0.5.1"
}
},
"oauth-sign": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
"integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM="
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1.0.2"
}
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"performance-now": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz",
"integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU="
},
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
},
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
},
"punycode": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
},
"qs": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz",
"integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM="
},
"request": {
"version": "2.81.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz",
"integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=",
"requires": {
"aws-sign2": "0.6.0",
"aws4": "1.6.0",
"caseless": "0.12.0",
"combined-stream": "1.0.5",
"extend": "3.0.1",
"forever-agent": "0.6.1",
"form-data": "2.1.4",
"har-validator": "4.2.1",
"hawk": "3.1.3",
"http-signature": "1.1.1",
"is-typedarray": "1.0.0",
"isstream": "0.1.2",
"json-stringify-safe": "5.0.1",
"mime-types": "2.1.17",
"oauth-sign": "0.8.2",
"performance-now": "0.2.0",
"qs": "6.4.0",
"safe-buffer": "5.1.1",
"stringstream": "0.0.5",
"tough-cookie": "2.3.3",
"tunnel-agent": "0.6.0",
"uuid": "3.1.0"
}
},
"rimraf": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
"integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
"requires": {
"glob": "7.1.2"
},
"dependencies": {
"glob": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"requires": {
"fs.realpath": "1.0.0",
"inflight": "1.0.6",
"inherits": "2.0.3",
"minimatch": "3.0.4",
"once": "1.4.0",
"path-is-absolute": "1.0.1"
}
}
}
},
"rxjs": {
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.2.tgz",
"integrity": "sha512-oRYoIKWBU3Ic37fLA5VJu31VqQO4bWubRntcHSJ+cwaDQBwdnZ9x4zmhJfm/nFQ2E82/I4loSioHnACamrKGgA==",
"requires": {
"symbol-observable": "1.1.0"
}
},
"rxjs-serial-subscription": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/rxjs-serial-subscription/-/rxjs-serial-subscription-0.1.1.tgz",
"integrity": "sha1-pCsdsL8QlLCSMRkeJ3jKP8+e0Uc=",
"requires": {
"rxjs": "5.5.2"
}
},
"safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"semver": {
"version": "5.4.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
"integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg=="
},
"sntp": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
"integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=",
"requires": {
"hoek": "2.16.3"
}
},
"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": {
"source-map": "0.6.1"
}
},
"spawn-rx": {
"version": "2.0.12",
"resolved": "https://registry.npmjs.org/spawn-rx/-/spawn-rx-2.0.12.tgz",
"integrity": "sha512-gOPXiQQFQ9lTOLuys0iMn3jfxxv9c7zzwhbYLOEbQGvEShHVJ5sSR1oD3Daj88os7jKArDYT7rbOKdvNhe7iEg==",
"requires": {
"debug": "2.6.9",
"lodash.assign": "4.2.0",
"rxjs": "5.5.2"
}
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
"sshpk": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz",
"integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=",
"requires": {
"asn1": "0.2.3",
"assert-plus": "1.0.0",
"bcrypt-pbkdf": "1.0.1",
"dashdash": "1.14.1",
"ecc-jsbn": "0.1.1",
"getpass": "0.1.7",
"jsbn": "0.1.1",
"tweetnacl": "0.14.5"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
}
}
},
"stringstream": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
"integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg="
},
"symbol-observable": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.1.0.tgz",
"integrity": "sha512-dQoid9tqQ+uotGhuTKEY11X4xhyYePVnqGSoSm3OGKh2E8LZ6RPULp1uXTctk33IeERlrRJYoVSBglsL05F5Uw=="
},
"tough-cookie": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz",
"integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=",
"requires": {
"punycode": "1.4.1"
}
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"requires": {
"safe-buffer": "5.1.1"
}
},
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
"optional": true
},
"underscore": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz",
"integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI="
},
"universalify": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz",
"integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc="
},
"untildify": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/untildify/-/untildify-3.0.2.tgz",
"integrity": "sha1-fx8wIFWz/qDz6B3HjrNnZstl4/E="
},
"uuid": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz",
"integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g=="
},
"verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
"requires": {
"assert-plus": "1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "1.3.0"
},
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
}
}
},
"winreg": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.2.tgz",
"integrity": "sha1-hQmvo7ccW70RCm18YkfsZ3NsWY8="
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"wurl": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/wurl/-/wurl-2.5.0.tgz",
"integrity": "sha1-g7qrSEi5hmnSFISg/NmjSrpXKOk="
},
"xmlhttprequest": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
"integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw="
},
"yallist": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
}
}
}

View File

@@ -1,14 +1,13 @@
{ {
"name": "zulip", "name": "zulip",
"productName": "Zulip", "productName": "Zulip",
"version": "1.5.0", "version": "1.7.0",
"description": "Zulip Desktop App", "description": "Zulip Desktop App",
"license": "Apache-2.0", "license": "Apache-2.0",
"email": "<svnitakash@gmail.com>", "copyright": "Kandra Labs, Inc.",
"copyright": "©2017 Kandra Labs, Inc.",
"author": { "author": {
"name": "Kandra Labs, Inc.", "name": "Kandra Labs, Inc.",
"email": "svnitakash@gmail.com" "email": "support@zulipchat.com"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -27,16 +26,14 @@
"InstantMessaging" "InstantMessaging"
], ],
"dependencies": { "dependencies": {
"electron-debug": "1.4.0", "auto-launch": "5.0.1",
"electron-is-dev": "0.3.0", "electron-is-dev": "0.3.0",
"electron-localshortcut": "2.0.2",
"electron-log": "2.2.7", "electron-log": "2.2.7",
"electron-spellchecker": "1.2.0", "electron-spellchecker": "1.1.2",
"electron-updater": "2.8.9", "electron-window-state": "4.1.1",
"electron-updater": "2.16.2",
"node-json-db": "0.7.3", "node-json-db": "0.7.3",
"request": "2.81.0", "request": "2.81.0",
"wurl": "2.5.0", "wurl": "2.5.0"
"electron-window-state": "4.1.1",
"auto-launch": "5.0.1"
} }
} }

View File

@@ -7,25 +7,37 @@
<body> <body>
<div class="about"> <div class="about">
<img class="logo" src="../resources/zulip.png" /> <img class="logo" src="../resources/zulip.png" />
<p class="detail" id="version">version ?.?.?</p> <p class="detail" id="version">v?.?.?</p>
<div class="maintenance-info"> <div class="maintenance-info">
<p class="detail maintainer">Maintained by Zulip</p> <p class="detail maintainer">
<p class="detail license">Available under the Apache License</p> Maintained by <a onclick="linkInBrowser('website')">Zulip</a>
<a class="bug" onclick="linkInBrowser()" href="#">Found bug?</a> </p>
<p class="detail license">
Available under the <a onclick="linkInBrowser('license')">Apache 2.0 License</a>
</p>
<a class="bug" onclick="linkInBrowser('bug')" href="#">Found bug?</a>
</div> </div>
</div> </div>
<script> <script>
const app = require('electron').remote.app; const { app } = require('electron').remote;
const version_tag = document.getElementById('version'); const { shell } = require('electron');
version_tag.innerHTML = 'version ' + app.getVersion(); const version_tag = document.querySelector('#version');
version_tag.innerHTML = 'v' + app.getVersion();
function linkInBrowser(event) {
const shell = require('electron').shell;
const url = "https://github.com/zulip/zulip-electron/issues/new?body=Please describe your issue and steps to reproduce it."
function linkInBrowser(type) {
let url;
switch (type) {
case 'website':
url = "https://zulipchat.com";
break;
case 'license':
url = "https://github.com/zulip/zulip-electron/blob/master/LICENSE";
break;
default:
url = 'https://github.com/zulip/zulip-electron/issues/new?body=' +
'%3C!--Please%20describe%20your%20issue%20and%20steps%20to%20reproduce%20it.--%3E';
}
shell.openExternal(url); shell.openExternal(url);
} }
</script> </script>

View File

@@ -1,21 +1,23 @@
body { body {
background: #fafafa; background: #fafafa;
font-family: menu, "Helvetica Neue", sans-serif; font-family: menu, "Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: subpixel-antialiased;
} }
.logo { .logo {
display: block; display: block;
margin: 0 auto; margin: -40px auto;
} }
#version { #version {
color: #aaa; color: #444343;
font-size: 0.9em; font-size: 1.3em;
padding-top: 40px;
} }
.about { .about {
margin-top: 50px; margin: 25vh auto;
height: 25vh;
text-align: center; text-align: center;
} }
@@ -42,9 +44,9 @@ body {
} }
.maintenance-info { .maintenance-info {
cursor: pointer;
position: absolute; position: absolute;
width: 100%; width: 100%;
bottom: 20px;
left: 0px; left: 0px;
color: #444; color: #444;
} }
@@ -52,7 +54,6 @@ body {
.maintenance-info p { .maintenance-info p {
margin: 0; margin: 0;
font-size: 1em; font-size: 1em;
width: 100%; width: 100%;
} }
@@ -71,3 +72,11 @@ body {
.maintenance-info .bug:hover { .maintenance-info .bug:hover {
background-color: #32a692; background-color: #32a692;
} }
p.detail a {
color: #355f4c;
}
p.detail a:hover {
text-decoration: underline;
}

View File

@@ -5,7 +5,7 @@ body {
cursor: default; cursor: default;
user-select: none; user-select: none;
font-family: menu, "Helvetica Neue", sans-serif; font-family: menu, "Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
font-size: 14px; font-size: 14px;
color: #333; color: #333;
background: #efefef; background: #efefef;
@@ -24,60 +24,65 @@ kbd {
white-space: nowrap; white-space: nowrap;
} }
table, th, td { table,
th,
td {
border-collapse: collapse; border-collapse: collapse;
color: #383430; color: #383430;
} }
table { table {
width: 100%; width: 100%;
margin-top: 18px; margin-top: 18px;
margin-bottom: 18px; margin-bottom: 18px;
} }
table tr:nth-child(even) { background-color: #f7eee6; } table tr:nth-child(even) {
background-color: #f7eee6;
}
table tr:nth-child(odd) { background-color: #fff8ef; } table tr:nth-child(odd) {
background-color: #fff8ef;
}
td {
td { padding: 5px; } padding: 5px;
}
td:nth-child(odd) { td:nth-child(odd) {
text-align: right; text-align: right;
width: 50%; width: 50%;
} }
@font-face { @font-face {
font-family: 'Material Icons'; font-family: 'Material Icons';
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: local('Material Icons'), src: local('Material Icons'), local('MaterialIcons-Regular'), url(../fonts/MaterialIcons-Regular.ttf) format('truetype');
local('MaterialIcons-Regular'),
url(../fonts/MaterialIcons-Regular.ttf) format('truetype');
} }
@font-face { @font-face {
font-family: 'Montserrat'; font-family: 'Montserrat';
src: url(../fonts/Montserrat-Regular.ttf) format('truetype'); src: url(../fonts/Montserrat-Regular.ttf) format('truetype');
} }
.material-icons { .material-icons {
font-family: 'Material Icons'; font-family: 'Material Icons';
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
/* Preferred icon size */ /* Preferred icon size */
font-size: 24px; font-size: 24px;
display: inline-block; display: inline-block;
line-height: 1; line-height: 1;
text-transform: none; text-transform: none;
letter-spacing: normal; letter-spacing: normal;
word-wrap: normal; word-wrap: normal;
white-space: nowrap; white-space: nowrap;
direction: ltr; direction: ltr;
/* Support for all WebKit browsers. */ /* Support for all WebKit browsers. */
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
/* Support for Safari and Chrome. */ /* Support for Safari and Chrome. */
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
} }
#content { #content {
@@ -140,9 +145,15 @@ td:nth-child(odd) {
.title { .title {
padding: 4px 0 6px 0; padding: 4px 0 6px 0;
font-weight: bold; font-weight: 500;
color: #222c31; color: #222c31;
text-transform: uppercase; }
.page-title {
color: #222c31;
font-size: 15px;
font-weight: bold;
padding: 4px 0 6px 0;
} }
.sub-title { .sub-title {
@@ -189,16 +200,18 @@ img.server-info-icon {
.setting-input-value { .setting-input-value {
flex-grow: 1; flex-grow: 1;
font-size: 14px; font-size: 14px;
height: 24px; height: 22px;
border: none; border-radius: 3px;
border-bottom: #ededed 1px solid; padding: 7px;
border: #ededed 2px solid;
outline-width: 0; outline-width: 0;
background: transparent; background: transparent;
max-width: 500px; max-width: 500px;
} }
.setting-input-value:focus { .setting-input-value:focus {
border-bottom: #7cb980 1px solid; border: #7cb980 2px solid;
border-radius: 3px;
} }
.setting-block { .setting-block {
@@ -224,7 +237,7 @@ img.server-info-icon {
.action i { .action i {
margin-right: 5px; margin-right: 5px;
font-size: 18px; font-size: 18px;
line-height: 27px; line-height: 26px;
} }
.settings-pane { .settings-pane {
@@ -315,75 +328,83 @@ i.open-tab-button {
} }
#server-info-container { #server-info-container {
min-height: calc(100% - 235px); min-height: calc(100% - 235px);
} }
#create-organization-container { #create-organization-container {
font-size: 1.15em; font-size: 1.15em;
margin-bottom: 15px; position: fixed;
bottom: 15px;
} }
#create-organization-container i { #create-organization-container i {
position: relative; position: relative;
top: 3px; top: 3px;
} }
#open-create-org-link { #open-create-org-link {
color: #666; color: #666;
cursor: pointer; cursor: pointer;
text-decoration: none; text-decoration: none;
} }
#open-create-org-link:hover { #open-create-org-link:hover {
color: #005580;; color: #005580;
text-decoration: underline; ;
text-decoration: underline;
} }
.toggle { .toggle {
position: absolute; position: absolute;
margin-left: -9999px; margin-left: -9999px;
visibility: hidden; visibility: hidden;
}
.toggle + label {
display: block;
position: relative;
cursor: pointer;
outline: none;
user-select: none;
} }
input.toggle-round + label { .toggle+label {
padding: 2px; display: block;
width: 50px; position: relative;
height: 25px; cursor: pointer;
background-color: #dddddd; outline: none;
border-radius: 25px; user-select: none;
} }
input.toggle-round + label:before,
input.toggle-round + label:after { input.toggle-round+label {
display: block; padding: 2px;
position: absolute; width: 50px;
top: 2px; height: 25px;
left: 2px; background-color: #dddddd;
bottom: 2px; border-radius: 25px;
content: "";
} }
input.toggle-round + label:before {
right: 2px; input.toggle-round+label:before,
background-color: #f1f1f1; input.toggle-round+label:after {
border-radius: 25px; display: block;
transition: background 0.4s; position: absolute;
top: 2px;
left: 2px;
bottom: 2px;
content: "";
} }
input.toggle-round + label:after {
width: 25px; input.toggle-round+label:before {
height: 25px; right: 2px;
background-color: #fff; background-color: #f1f1f1;
border-radius: 100%; border-radius: 25px;
transition: margin 0.4s; transition: background 0.4s;
} }
input.toggle-round:checked + label:before {
background-color: #4EBFAC; input.toggle-round+label:after {
width: 25px;
height: 25px;
background-color: #fff;
border-radius: 100%;
transition: margin 0.4s;
} }
input.toggle-round:checked + label:after {
margin-left: 25px; input.toggle-round:checked+label:before {
background-color: #4EBFAC;
}
input.toggle-round:checked+label:after {
margin-left: 25px;
} }

View File

@@ -4,12 +4,14 @@ const path = require('path');
const fs = require('fs'); const fs = require('fs');
const DomainUtil = require(__dirname + '/../utils/domain-util.js'); 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 SystemUtil = require(__dirname + '/../utils/system-util.js');
const LinkUtil = require(__dirname + '/../utils/link-util.js'); const LinkUtil = require(__dirname + '/../utils/link-util.js');
const { shell, app } = require('electron').remote; const { shell, app } = require('electron').remote;
const BaseComponent = require(__dirname + '/../components/base.js'); const BaseComponent = require(__dirname + '/../components/base.js');
const shouldSilentWebview = ConfigUtil.getConfigItem('silent');
class WebView extends BaseComponent { class WebView extends BaseComponent {
constructor(props) { constructor(props) {
super(); super();
@@ -54,6 +56,12 @@ class WebView extends BaseComponent {
} }
}); });
if (shouldSilentWebview) {
this.$el.addEventListener('dom-ready', () => {
this.$el.setAudioMuted(true);
});
}
this.$el.addEventListener('page-title-updated', event => { this.$el.addEventListener('page-title-updated', event => {
const { title } = event; const { title } = event;
this.badgeCount = this.getBadgeCount(title); this.badgeCount = this.getBadgeCount(title);

View File

@@ -1,10 +1,10 @@
'use strict'; 'use strict';
require(__dirname + '/js/tray.js');
const { ipcRenderer, remote } = require('electron'); const { ipcRenderer, remote } = require('electron');
const { session } = remote; const { session } = remote;
require(__dirname + '/js/tray.js');
const DomainUtil = require(__dirname + '/js/utils/domain-util.js'); const DomainUtil = require(__dirname + '/js/utils/domain-util.js');
const WebView = require(__dirname + '/js/components/webview.js'); const WebView = require(__dirname + '/js/components/webview.js');
const ServerTab = require(__dirname + '/js/components/server-tab.js'); const ServerTab = require(__dirname + '/js/components/server-tab.js');
@@ -43,6 +43,7 @@ class ServerManagerView {
this.initTabs(); this.initTabs();
this.initActions(); this.initActions();
this.registerIpcs(); this.registerIpcs();
this.initDefaultSettings();
}); });
} }
@@ -65,6 +66,39 @@ class ServerManagerView {
}); });
} }
// Settings are initialized only when user clicks on General/Server/Network section settings
// In case, user doesn't visit these section, those values set to be null automatically
// This will make sure the default settings are correctly set to either true or false
initDefaultSettings() {
// Default settings which should be respected
const settingOptions = {
trayIcon: true,
useProxy: false,
showSidebar: true,
badgeOption: true,
startAtLogin: false,
startMinimized: false,
enableSpellchecker: true,
showNotification: true,
betaUpdate: false,
silent: false,
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]);
}
}
}
initSidebar() { initSidebar() {
const showSidebar = ConfigUtil.getConfigItem('showSidebar', true); const showSidebar = ConfigUtil.getConfigItem('showSidebar', true);
this.toggleSidebar(showSidebar); this.toggleSidebar(showSidebar);
@@ -83,7 +117,6 @@ class ServerManagerView {
} else { } else {
this.openSettings('Servers'); this.openSettings('Servers');
} }
ipcRenderer.send('local-shortcuts', true);
} }
initServer(server, index) { initServer(server, index) {
@@ -122,6 +155,13 @@ class ServerManagerView {
this.openSettings('General'); this.openSettings('General');
}); });
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.$addServerButton, this.$addServerTooltip);
this.sidebarHoverEvent(this.$settingsButton, this.$settingsTooltip); this.sidebarHoverEvent(this.$settingsButton, this.$settingsTooltip);
this.sidebarHoverEvent(this.$reloadButton, this.$reloadTooltip); this.sidebarHoverEvent(this.$reloadButton, this.$reloadTooltip);
@@ -230,6 +270,27 @@ class ServerManagerView {
tabs: this.tabs, tabs: this.tabs,
activeTabIndex: this.activeTabIndex 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) { destroyTab(name, index) {
@@ -257,9 +318,6 @@ class ServerManagerView {
// Clear DOM elements // Clear DOM elements
this.$tabsContainer.innerHTML = ''; this.$tabsContainer.innerHTML = '';
this.$webviewsContainer.innerHTML = ''; this.$webviewsContainer.innerHTML = '';
// Destroy shortcuts
ipcRenderer.send('local-shortcuts', false);
} }
reloadView() { reloadView() {

View File

@@ -1,10 +1,10 @@
'use strict'; 'use strict';
const { remote } = require('electron'); const { remote, ipcRenderer } = require('electron');
const ConfigUtil = require(__dirname + '/utils/config-util.js'); const ConfigUtil = require(__dirname + '/utils/config-util.js');
const app = remote.app; const { app } = remote;
// From https://github.com/felixrieseberg/electron-windows-notifications#appusermodelid // From https://github.com/felixrieseberg/electron-windows-notifications#appusermodelid
// On windows 8 we have to explicitly set the appUserModelId otherwise notification won't work. // On windows 8 we have to explicitly set the appUserModelId otherwise notification won't work.
@@ -16,6 +16,10 @@ class baseNotification extends NativeNotification {
constructor(title, opts) { constructor(title, opts) {
opts.silent = ConfigUtil.getConfigItem('silent') || false; opts.silent = ConfigUtil.getConfigItem('silent') || false;
super(title, opts); super(title, opts);
this.addEventListener('click', () => {
ipcRenderer.send('focus-app');
});
} }
static requestPermission() { static requestPermission() {
return; // eslint-disable-line no-useless-return return; // eslint-disable-line no-useless-return

View File

@@ -45,7 +45,7 @@ class BadgeSettings {
updateOverlayIcon(messageCount, mainWindow) { updateOverlayIcon(messageCount, mainWindow) {
if (!mainWindow.isFocused()) { if (!mainWindow.isFocused()) {
mainWindow.flashFrame(true); mainWindow.flashFrame(ConfigUtil.getConfigItem('flashTaskbarOnMessage'));
} }
if (messageCount === 0) { if (messageCount === 0) {
mainWindow.setOverlayIcon(null, ''); mainWindow.setOverlayIcon(null, '');

View File

@@ -13,7 +13,7 @@ class CreateOrganziation extends BaseComponent {
return ` return `
<div class="setting-row"> <div class="setting-row">
<div class="setting-description"> <div class="setting-description">
<span id="open-create-org-link">Create a new organization on zulipchat.com<i class="material-icons open-tab-button">open_in_new</i></span> <span id="open-create-org-link">Or create a new organization on zulipchat.com<i class="material-icons open-tab-button">open_in_new</i></span>
</div> </div>
<div class="setting-control"></div> <div class="setting-control"></div>
</div> </div>

View File

@@ -1,11 +1,10 @@
'use strict'; 'use strict';
const path = require('path'); 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 fs = require('fs-extra');
const { app, dialog } = remote;
const currentBrowserWindow = remote.getCurrentWindow();
const BaseSection = require(__dirname + '/base-section.js'); const BaseSection = require(__dirname + '/base-section.js');
const ConfigUtil = require(__dirname + '/../../utils/config-util.js'); const ConfigUtil = require(__dirname + '/../../utils/config-util.js');
@@ -29,9 +28,13 @@ class GeneralSection extends BaseSection {
<div class="setting-control"></div> <div class="setting-control"></div>
</div> </div>
<div class="setting-row" id="badge-option"> <div class="setting-row" id="badge-option">
<div class="setting-description">Show app unread badge</div> <div class="setting-description">Show app unread badge</div>
<div class="setting-control"></div> <div class="setting-control"></div>
</div> </div>
<div class="setting-row" id="flash-taskbar-option" style= "display:${process.platform === 'win32' ? '' : 'none'}">
<div class="setting-description">Flash taskbar on new message</div>
<div class="setting-control"></div>
</div>
</div> </div>
<div class="title">Desktop Notification</div> <div class="title">Desktop Notification</div>
<div class="settings-card"> <div class="settings-card">
@@ -57,6 +60,14 @@ class GeneralSection extends BaseSection {
<div class="setting-description">Start app at login</div> <div class="setting-description">Start app at login</div>
<div class="setting-control"></div> <div class="setting-control"></div>
</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> </div>
<div class="title">Reset Application Data</div> <div class="title">Reset Application Data</div>
<div class="settings-card"> <div class="settings-card">
@@ -74,12 +85,20 @@ class GeneralSection extends BaseSection {
this.props.$root.innerHTML = this.template(); this.props.$root.innerHTML = this.template();
this.updateTrayOption(); this.updateTrayOption();
this.updateBadgeOption(); this.updateBadgeOption();
this.updateUpdateOption();
this.updateSilentOption(); this.updateSilentOption();
this.updateUpdateOption();
this.updateSidebarOption(); this.updateSidebarOption();
this.updateStartAtLoginOption(); this.updateStartAtLoginOption();
this.updateResetDataOption(); this.updateResetDataOption();
this.showDesktopNotification(); this.showDesktopNotification();
this.enableSpellchecker();
this.minimizeOnStart();
// Platform specific settings
// Flashing taskbar on Windows
if (process.platform === 'win32') {
this.updateFlashTaskbar();
}
} }
updateTrayOption() { updateTrayOption() {
@@ -108,6 +127,18 @@ class GeneralSection extends BaseSection {
}); });
} }
updateFlashTaskbar() {
this.generateSettingOption({
$element: document.querySelector('#flash-taskbar-option .setting-control'),
value: ConfigUtil.getConfigItem('flashTaskbarOnMessage', true),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('flashTaskbarOnMessage');
ConfigUtil.setConfigItem('flashTaskbarOnMessage', newValue);
this.updateFlashTaskbar();
}
});
}
updateUpdateOption() { updateUpdateOption() {
this.generateSettingOption({ this.generateSettingOption({
$element: document.querySelector('#betaupdate-option .setting-control'), $element: document.querySelector('#betaupdate-option .setting-control'),
@@ -128,6 +159,7 @@ class GeneralSection extends BaseSection {
const newValue = !ConfigUtil.getConfigItem('silent', true); const newValue = !ConfigUtil.getConfigItem('silent', true);
ConfigUtil.setConfigItem('silent', newValue); ConfigUtil.setConfigItem('silent', newValue);
this.updateSilentOption(); this.updateSilentOption();
currentBrowserWindow.send('toogle-silent', newValue);
} }
}); });
} }
@@ -170,6 +202,18 @@ class GeneralSection extends BaseSection {
}); });
} }
enableSpellchecker() {
this.generateSettingOption({
$element: document.querySelector('#enable-spellchecker-option .setting-control'),
value: ConfigUtil.getConfigItem('enableSpellchecker', true),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('enableSpellchecker');
ConfigUtil.setConfigItem('enableSpellchecker', newValue);
this.enableSpellchecker();
}
});
}
clearAppDataDialog() { clearAppDataDialog() {
const clearAppDataMessage = 'By clicking proceed you will be removing all added accounts and preferences from Zulip. When the application restarts, it will be as if you are starting Zulip for the first time.'; const clearAppDataMessage = 'By clicking proceed you will be removing all added accounts and preferences from Zulip. When the application restarts, it will be as if you are starting Zulip for the first time.';
const getAppPath = path.join(app.getPath('appData'), app.getName()); const getAppPath = path.join(app.getPath('appData'), app.getName());
@@ -195,6 +239,18 @@ 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; module.exports = GeneralSection;

View File

@@ -13,12 +13,13 @@ class NewServerForm extends BaseComponent {
return ` return `
<div class="settings-card"> <div class="settings-card">
<div class="server-info-right"> <div class="server-info-right">
<div class="title">URL of Zulip organization</div>
<div class="server-info-row"> <div class="server-info-row">
<input class="setting-input-value" autofocus placeholder="Enter the URL of your Zulip organization..."/> <input class="setting-input-value" autofocus placeholder="acme.zulipchat.com or chat.acme.com"/>
</div> </div>
<div class="server-info-row"> <div class="server-info-row">
<div class="action blue server-save-action"> <div class="action blue server-save-action">
<i class="material-icons">check_box</i> <i class="material-icons">add_box</i>
<span>Add</span> <span>Add</span>
</div> </div>
</div> </div>

View File

@@ -1,7 +1,7 @@
'use strict'; 'use strict';
const BaseComponent = require(__dirname + '/js/components/base.js'); const BaseComponent = require(__dirname + '/js/components/base.js');
const {ipcRenderer} = require('electron'); const { ipcRenderer } = require('electron');
const Nav = require(__dirname + '/js/pages/preference/nav.js'); const Nav = require(__dirname + '/js/pages/preference/nav.js');
const ServersSection = require(__dirname + '/js/pages/preference/servers-section.js'); const ServersSection = require(__dirname + '/js/pages/preference/servers-section.js');
@@ -73,6 +73,18 @@ class PreferenceView extends BaseComponent {
ipcRenderer.on('switch-settings-nav', (event, navItem) => { ipcRenderer.on('switch-settings-nav', (event, navItem) => {
this.handleNavigation(navItem); this.handleNavigation(navItem);
}); });
ipcRenderer.on('toggle-sidebar', (event, state) => {
const inputSelector = '#sidebar-option .action .switch input';
const input = document.querySelector(inputSelector);
input.checked = state;
});
ipcRenderer.on('toggletray', (event, state) => {
const inputSelector = '#tray-option .action .switch input';
const input = document.querySelector(inputSelector);
input.checked = state;
});
} }
} }

View File

@@ -20,7 +20,7 @@ class ServerInfoForm extends BaseComponent {
<div class="server-info-right"> <div class="server-info-right">
<div class="server-info-row"> <div class="server-info-row">
<span class="server-info-alias">${this.props.server.alias}</span> <span class="server-info-alias">${this.props.server.alias}</span>
<i class="material-icons open-tab-button">open_in_new</i> <i class="material-icons open-tab-button">open_in_new</i>
</div> </div>
<div class="server-info-row"> <div class="server-info-row">
<input class="setting-input-value" disabled value="${this.props.server.url}"/> <input class="setting-input-value" disabled value="${this.props.server.url}"/>

View File

@@ -15,7 +15,7 @@ class ServersSection extends BaseSection {
template() { template() {
return ` return `
<div class="settings-pane" id="server-settings-pane"> <div class="settings-pane" id="server-settings-pane">
<div class="title">Add Server</div> <div class="page-title">Register or login to a Zulip organization to get started</div>
<div id="new-server-container"></div> <div id="new-server-container"></div>
<div class="title" id="existing-servers"></div> <div class="title" id="existing-servers"></div>
<div id="server-info-container"></div> <div id="server-info-container"></div>
@@ -38,9 +38,9 @@ class ServersSection extends BaseSection {
this.$newServerContainer = document.getElementById('new-server-container'); this.$newServerContainer = document.getElementById('new-server-container');
this.$newServerButton = document.getElementById('new-server-action'); this.$newServerButton = document.getElementById('new-server-action');
this.$serverInfoContainer.innerHTML = servers.length ? '' : 'Add your first server to get started!'; this.$serverInfoContainer.innerHTML = servers.length ? '' : '';
// Show Existing servers if servers are there otherwise hide it // Show Existing servers if servers are there otherwise hide it
this.$existingServers.innerHTML = servers.length === 0 ? '' : 'Existing Servers'; this.$existingServers.innerHTML = servers.length === 0 ? '' : 'Existing organizations';
this.initNewServerForm(); this.initNewServerForm();
this.$createOrganizationContainer = document.getElementById('create-organization-container'); this.$createOrganizationContainer = document.getElementById('create-organization-container');

View File

@@ -1,7 +1,10 @@
'use strict'; 'use strict';
const { ipcRenderer } = require('electron'); const { ipcRenderer } = require('electron');
const { spellChecker } = require('./spellchecker'); const SetupSpellChecker = require('./spellchecker');
const ConfigUtil = require(__dirname + '/utils/config-util.js');
// eslint-disable-next-line import/no-unassigned-import // eslint-disable-next-line import/no-unassigned-import
require('./notification'); require('./notification');
@@ -32,8 +35,15 @@ process.once('loaded', () => {
// To prevent failing this script on linux we need to load it after the document loaded // To prevent failing this script on linux we need to load it after the document loaded
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
// Init spellchecker // Get the default language of the server
spellChecker(); const serverLanguage = page_params.default_language; // eslint-disable-line no-undef, camelcase
if (serverLanguage) {
// Set spellcheker language
ConfigUtil.setConfigItem('spellcheckerLanguage', serverLanguage);
// Init spellchecker
SetupSpellChecker.init();
}
// redirect users to network troubleshooting page // redirect users to network troubleshooting page
const getRestartButton = document.querySelector('.restart_get_events_button'); const getRestartButton = document.querySelector('.restart_get_events_button');
@@ -43,3 +53,10 @@ document.addEventListener('DOMContentLoaded', () => {
}); });
} }
}); });
// Clean up spellchecker events after you navigate away from this page;
// otherwise, you may experience errors
window.addEventListener('beforeunload', () => {
SetupSpellChecker.unsubscribeSpellChecker();
});

View File

@@ -1,29 +1,56 @@
'use strict'; 'use strict';
const {SpellCheckHandler, ContextMenuListener, ContextMenuBuilder} = require('electron-spellchecker'); const { SpellCheckHandler, ContextMenuListener, ContextMenuBuilder } = require('electron-spellchecker');
function spellChecker() { const ConfigUtil = require(__dirname + '/utils/config-util.js');
// Implement spellcheck using electron api
window.spellCheckHandler = new SpellCheckHandler();
window.spellCheckHandler.attachToInput();
// Start off as US English class SetupSpellChecker {
window.spellCheckHandler.switchLanguage('en-US'); init() {
if (ConfigUtil.getConfigItem('enableSpellchecker')) {
this.enableSpellChecker();
}
this.enableContextMenu();
}
const contextMenuBuilder = new ContextMenuBuilder(window.spellCheckHandler); enableSpellChecker() {
const contextMenuListener = new ContextMenuListener(info => { try {
contextMenuBuilder.showPopupMenu(info); this.SpellCheckHandler = new SpellCheckHandler();
}); } catch (err) {
console.log(err);
}
}
// Clean up events after you navigate away from this page; enableContextMenu() {
// otherwise, you may experience errors if (this.SpellCheckHandler) {
window.addEventListener('beforeunload', () => { this.SpellCheckHandler.attachToInput();
// eslint-disable-next-line no-undef
spellCheckHandler.unsubscribe(); const userLanguage = ConfigUtil.getConfigItem('spellcheckerLanguage');
contextMenuListener.unsubscribe();
}); // eslint-disable-next-line no-unused-expressions
process.platform === 'darwin' ?
// On macOS, spellchecker fails to auto-detect the lanugage user is typing in
// that's why we need to mention it explicitly
this.SpellCheckHandler.switchLanguage(userLanguage) :
// On Linux and Windows, spellchecker can automatically detects the language the user is typing in
// and silently switches on the fly; thus we can start off as US English
this.SpellCheckHandler.switchLanguage('en-US');
}
const contextMenuBuilder = new ContextMenuBuilder(this.SpellCheckHandler);
this.contextMenuListener = new ContextMenuListener(info => {
contextMenuBuilder.showPopupMenu(info);
});
}
unsubscribeSpellChecker() {
// eslint-disable-next-line no-undef
if (this.SpellCheckHandler) {
this.SpellCheckHandler.unsubscribe();
}
if (this.contextMenuListener) {
this.contextMenuListener.unsubscribe();
}
}
} }
module.exports = { module.exports = new SetupSpellChecker();
spellChecker
};

View File

@@ -3,105 +3,111 @@ const path = require('path');
const electron = require('electron'); const electron = require('electron');
const {ipcRenderer, remote} = electron; const { ipcRenderer, remote } = electron;
const {Tray, Menu, nativeImage, BrowserWindow} = remote; const { Tray, Menu, BrowserWindow } = remote;
const APP_ICON = path.join(__dirname, '../../resources/tray', 'tray'); const APP_ICON = path.join(__dirname, '../../resources/', 'f');
const ConfigUtil = require(__dirname + '/utils/config-util.js'); const ConfigUtil = require(__dirname + '/utils/config-util.js');
const iconPath = () => { const iconPath = unreadCount => {
if (process.platform === 'linux') { if (process.platform === 'linux') {
return APP_ICON + 'linux.png'; return APP_ICON + 'linux.png';
} }
return APP_ICON + (process.platform === 'win32' ? 'win.ico' : 'osx.png'); if (!unreadCount) {
return path.join(__dirname, '../../resources/tray', 'trayosx@2x.png');
}
if (unreadCount > 99) {
return APP_ICON + (process.platform === 'win32' ? 'win.ico' : `/favicon-infinite.png`);
}
return APP_ICON + (process.platform === 'win32' ? 'win.ico' : `/favicon-${unreadCount}.png`);
}; };
let unread = 0; let unread = 0;
const trayIconSize = () => { // const trayIconSize = () => {
switch (process.platform) { // switch (process.platform) {
case 'darwin': // case 'darwin':
return 20; // return 20;
case 'win32': // case 'win32':
return 100; // return 100;
case 'linux': // case 'linux':
return 100; // return 100;
default: return 80; // default: return 80;
} // }
}; // };
// Default config for Icon we might make it OS specific if needed like the size // Default config for Icon we might make it OS specific if needed like the size
const config = { // const config = {
pixelRatio: window.devicePixelRatio, // pixelRatio: window.devicePixelRatio,
unreadCount: 0, // unreadCount: 0,
showUnreadCount: true, // showUnreadCount: true,
unreadColor: '#000000', // unreadColor: '#000000',
readColor: '#000000', // readColor: '#000000',
unreadBackgroundColor: '#B9FEEA', // unreadBackgroundColor: '#B9FEEA',
readBackgroundColor: '#B9FEEA', // readBackgroundColor: '#B9FEEA',
size: trayIconSize(), // size: trayIconSize(),
thick: process.platform === 'win32' // thick: process.platform === 'win32'
}; // };
const renderCanvas = function (arg) { // const renderCanvas = function (arg) {
config.unreadCount = arg; // config.unreadCount = arg;
return new Promise(resolve => { // return new Promise(resolve => {
const SIZE = config.size * config.pixelRatio; // const SIZE = config.size * config.pixelRatio;
const PADDING = SIZE * 0.05; // const PADDING = SIZE * 0.05;
const CENTER = SIZE / 2; // const CENTER = SIZE / 2;
const HAS_COUNT = config.showUnreadCount && config.unreadCount; // const HAS_COUNT = config.showUnreadCount && config.unreadCount;
const color = config.unreadCount ? config.unreadColor : config.readColor; // const color = config.unreadCount ? config.unreadColor : config.readColor;
const backgroundColor = config.unreadCount ? config.unreadBackgroundColor : config.readBackgroundColor; // const backgroundColor = config.unreadCount ? config.unreadBackgroundColor : config.readBackgroundColor;
const canvas = document.createElement('canvas'); // const canvas = document.createElement('canvas');
canvas.width = SIZE; // canvas.width = SIZE;
canvas.height = SIZE; // canvas.height = SIZE;
const ctx = canvas.getContext('2d'); // const ctx = canvas.getContext('2d');
// Circle // // Circle
// If (!config.thick || config.thick && HAS_COUNT) { // // If (!config.thick || config.thick && HAS_COUNT) {
ctx.beginPath(); // ctx.beginPath();
ctx.arc(CENTER, CENTER, (SIZE / 2) - PADDING, 0, 2 * Math.PI, false); // ctx.arc(CENTER, CENTER, (SIZE / 2) - PADDING, 0, 2 * Math.PI, false);
ctx.fillStyle = backgroundColor; // ctx.fillStyle = backgroundColor;
ctx.fill(); // ctx.fill();
ctx.lineWidth = SIZE / (config.thick ? 10 : 20); // ctx.lineWidth = SIZE / (config.thick ? 10 : 20);
ctx.strokeStyle = backgroundColor; // ctx.strokeStyle = backgroundColor;
ctx.stroke(); // ctx.stroke();
// Count or Icon // // Count or Icon
if (HAS_COUNT) { // if (HAS_COUNT) {
ctx.fillStyle = color; // ctx.fillStyle = color;
ctx.textAlign = 'center'; // ctx.textAlign = 'center';
if (config.unreadCount > 99) { // if (config.unreadCount > 99) {
ctx.font = `${config.thick ? 'bold ' : ''}${SIZE * 0.4}px Helvetica`; // ctx.font = `${config.thick ? 'bold ' : ''}${SIZE * 0.4}px Helvetica`;
ctx.fillText('99+', CENTER, CENTER + (SIZE * 0.15)); // ctx.fillText('99+', CENTER, CENTER + (SIZE * 0.15));
} else if (config.unreadCount < 10) { // } else if (config.unreadCount < 10) {
ctx.font = `${config.thick ? 'bold ' : ''}${SIZE * 0.5}px Helvetica`; // ctx.font = `${config.thick ? 'bold ' : ''}${SIZE * 0.5}px Helvetica`;
ctx.fillText(config.unreadCount, CENTER, CENTER + (SIZE * 0.20)); // ctx.fillText(config.unreadCount, CENTER, CENTER + (SIZE * 0.20));
} else { // } else {
ctx.font = `${config.thick ? 'bold ' : ''}${SIZE * 0.5}px Helvetica`; // ctx.font = `${config.thick ? 'bold ' : ''}${SIZE * 0.5}px Helvetica`;
ctx.fillText(config.unreadCount, CENTER, CENTER + (SIZE * 0.15)); // ctx.fillText(config.unreadCount, CENTER, CENTER + (SIZE * 0.15));
} // }
resolve(canvas); // resolve(canvas);
} // }
}); // });
}; // };
/** // /**
* Renders the tray icon as a native image // * Renders the tray icon as a native image
* @param arg: Unread count // * @param arg: Unread count
* @return the native image // * @return the native image
*/ // */
const renderNativeImage = function (arg) { // const renderNativeImage = function (arg) {
return Promise.resolve() // return Promise.resolve()
.then(() => renderCanvas(arg)) // .then(() => iconPath(arg))
.then(canvas => { // // .then(canvas => {
const pngData = nativeImage.createFromDataURL(canvas.toDataURL('image/png')).toPng(); // // const pngData = nativeImage.createFromDataURL(canvas.toDataURL('image/png')).toPng();
return Promise.resolve(nativeImage.createFromBuffer(pngData, config.pixelRatio)); // // return Promise.resolve(nativeImage.createFromBuffer(pngData, config.pixelRatio));
}); // // });
}; // };
function sendAction(action) { function sendAction(action) {
const win = BrowserWindow.getAllWindows()[0]; const win = BrowserWindow.getAllWindows()[0];
@@ -181,38 +187,45 @@ ipcRenderer.on('tray', (event, arg) => {
return; return;
} }
// We don't want to create tray from unread messages on macOS since it already has dock badges. // We don't want to create tray from unread messages on macOS since it already has dock badges.
if (process.platform === 'linux' || process.platform === 'win32') { if (process.platform === 'darwin' || process.platform === 'win32') {
if (arg === 0) { if (arg === 0) {
unread = arg; unread = arg;
window.tray.setImage(iconPath()); window.tray.setImage(iconPath());
window.tray.setToolTip('No unread messages'); window.tray.setToolTip('No unread messages');
} else { } else {
unread = arg; unread = arg;
renderNativeImage(arg).then(image => { // renderNativeImage(arg).then(image => {
window.tray.setImage(image); window.tray.setImage(iconPath(arg));
window.tray.setToolTip(arg + ' unread messages'); window.tray.setToolTip(arg + ' unread messages');
}); // });
} }
} }
}); });
function toggleTray() { function toggleTray() {
let state;
if (window.tray) { if (window.tray) {
state = false;
window.tray.destroy(); window.tray.destroy();
if (window.tray.isDestroyed()) { if (window.tray.isDestroyed()) {
window.tray = null; window.tray = null;
} }
ConfigUtil.setConfigItem('trayIcon', false); ConfigUtil.setConfigItem('trayIcon', false);
} else { } else {
state = true;
createTray(); createTray();
if (process.platform === 'linux' || process.platform === 'win32') { if (process.platform === 'darwin' || process.platform === 'win32') {
renderNativeImage(unread).then(image => { // renderNativeImage(unread).then(image => {
window.tray.setImage(image); window.tray.setImage(iconPath());
window.tray.setToolTip(unread + ' unread messages'); window.tray.setToolTip(unread + ' unread messages');
}); // });
} }
ConfigUtil.setConfigItem('trayIcon', true); 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); ipcRenderer.on('toggletray', toggleTray);

View File

@@ -1,16 +1,29 @@
'use strict'; 'use strict';
const fs = require('fs');
const path = require('path');
const process = require('process'); const process = require('process');
const JsonDB = require('node-json-db'); 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 instance = null;
let dialog = null;
let app = null; let app = null;
/* To make the util runnable in both main and renderer process */ /* To make the util runnable in both main and renderer process */
if (process.type === 'renderer') { if (process.type === 'renderer') {
app = require('electron').remote.app; const remote = require('electron').remote;
dialog = remote.dialog;
app = remote.app;
} else { } else {
app = require('electron').app; const electron = require('electron');
dialog = electron.dialog;
app = electron.app;
} }
class ConfigUtil { class ConfigUtil {
@@ -47,7 +60,22 @@ class ConfigUtil {
} }
reloadDB() { reloadDB() {
this.db = new JsonDB(app.getPath('userData') + '/settings.json', true, true); 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);
} }
} }

View File

@@ -0,0 +1,31 @@
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,6 +5,12 @@ const fs = require('fs');
const path = require('path'); const path = require('path');
const JsonDB = require('node-json-db'); const JsonDB = require('node-json-db');
const request = require('request'); const request = require('request');
const Logger = require('./logger-util');
const logger = new Logger({
file: `domain-util.log`,
timestamp: true
});
let instance = null; let instance = null;
@@ -110,17 +116,28 @@ class DomainUtil {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
request(checkDomain, (error, response) => { request(checkDomain, (error, response) => {
const certsError = const certsError =
['Error: self signed certificate', [
'Error: unable to verify the first certificate' 'Error: self signed certificate',
'Error: unable to verify the first certificate',
'Error: unable to get local issuer certificate'
]; ];
if (!error && response.statusCode !== 404) {
// 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) {
// Correct // Correct
this.getServerSettings(domain).then(serverSettings => { this.getServerSettings(domain).then(serverSettings => {
resolve(serverSettings); resolve(serverSettings);
}, () => { }, () => {
resolve(serverConf); resolve(serverConf);
}); });
} else if (certsError.indexOf(error.toString()) >= 0) { } else if (domain.indexOf(whitelistDomains) >= 0 || certsError.indexOf(error.toString()) >= 0) {
if (silent) { if (silent) {
this.getServerSettings(domain).then(serverSettings => { this.getServerSettings(domain).then(serverSettings => {
resolve(serverSettings); resolve(serverSettings);
@@ -152,7 +169,9 @@ class DomainUtil {
}); });
} }
} else { } else {
reject('Not a valid Zulip server'); 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`;
reject(invalidZulipServerError);
} }
}); });
}); });
@@ -217,7 +236,23 @@ class DomainUtil {
} }
reloadDB() { reloadDB() {
this.db = new JsonDB(app.getPath('userData') + '/domain.json', true, true); 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);
} }
generateFilePath(url) { generateFilePath(url) {

View File

@@ -0,0 +1,87 @@
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

@@ -12,5 +12,5 @@
<div id="settings-container"></div> <div id="settings-container"></div>
</div> </div>
</body> </body>
<script src="js/pages/preference/preference.js"></script> <script src="js/pages/preference/preference.js"></script>
</html> </html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Some files were not shown because too many files have changed in this diff Show More