Compare commits

..

58 Commits

Author SHA1 Message Date
Akash Nimare
68dd63d472 travis: Test dist path [WIP]. 2018-03-15 19:49:03 +05:30
Akash Nimare
d2f53a0b71 travis: Test dist path. 2018-03-15 19:32:56 +05:30
Akash Nimare
fba0330512 travis: Debug macOS installers. 2018-03-15 18:20:49 +05:30
Akash Nimare
4ff914cf34 travis: Enable debugging for artifacts. 2018-03-15 17:03:09 +05:30
Akash Nimare
4831bea447 travis: Update build script. 2018-03-15 16:40:56 +05:30
Akash Nimare
01849bf601 electron-builder: Update builder to latest release. 2018-03-13 19:48:52 +05:30
cPhost
349294f536 travis: Upload build artifacts for release branch. 2018-03-09 15:00:34 +05:30
Akash Nimare
435e5f086e webview: Fix webview flickering. (#439)
Rewrote styling for the webview so that it takes the whole window
when the app loads up.

Fixes #249.
2018-03-09 14:35:13 +05:30
Akash Nimare
603ad7dfcd setting-page: Make setting navs better. 2018-03-07 21:00:30 +05:30
Akash Nimare
a47a0e3e90 server-page: Improve add new server page.
WIP, #340.
2018-03-06 17:45:00 +05:30
Priyank P
8e04920f54 il8n: Add locale helper script. (#437)
This script automatically builds locales if the locale-template is changed.
2018-03-06 05:08:24 +05:30
Abhigyan Khaund
4a7b84e483 menu: Add "What's new" in help submenu. 2018-03-06 04:49:51 +05:30
Akash Nimare
ffd2ee36fa left-sidebar : Fix position of left-sidebar on toggel. 2018-03-01 21:36:13 +05:30
Akash Nimare
c11fa93642 left-sidebar: Fix tooltip not visible on hover.
Regression from c11089027c.
2018-03-01 21:12:07 +05:30
Akash Nimare
a90dc0c82f Custom css: Add a setting option for custom css.
This PR adds an option to inject custom CSS.
Fixes - #432.
2018-03-01 18:52:53 +05:30
Akash Nimare
028bc02d0a Downgrade node to 6.
Looks like the app can't be packaged on node > 8. This must be an issue with electron-builder.
Downgrading until the fix comes.
2018-03-01 04:37:00 +05:30
Akash Nimare
d673d5b76c 🎉 v1.8.2. 2018-02-26 19:18:23 +05:30
Akash Nimare
2bf88aa912 webview: Remove loading indicator once webview is loaded. 2018-02-26 18:36:25 +05:30
Akash Nimare
c9f179a2ae node: Enforce LTS node v8.0.0. 2018-02-26 18:28:16 +05:30
Akash Nimare
c11089027c left-sidebar: Align action container properly. 2018-02-26 18:09:42 +05:30
Abhigyan Khaund
5c45ab7b66 fix: New organization link overlapping existing servers.
Fixes: #427.
2018-02-26 17:28:59 +05:30
Akash Nimare
6205ca0aca Update shortcut for opening webapp keyboard shortcuts. 2018-02-24 01:27:28 +05:30
Abhigyan Khaund
b83e2dd428 settings-tab: Do not create a sidebar tab rather highlight bottom settings icon. (#422)
Creating the new functional tab for setting tab was unnecessary as we already have a sticky setting icon at the bottom. This PR adds a functionality to highlight that settings icon instead of creating the new one.

Fixes: #418.
2018-02-23 19:03:30 +05:30
Akash Nimare
fd421a62d2 webview: Improve flickering of webview on first load. 2018-02-23 02:03:18 +05:30
Abhigyan Khaund
fa85241c79 design: Only show the back button when needed.
Fixes: #421.
2018-02-23 00:53:23 +05:30
Akash Nimare
b163c237b6 Do not auto-reload app when system comes back from sleep.
Improves #412, #312.
2018-02-22 20:55:55 +05:30
Abhigyan Khaund
5351ee10fa auto-updates: Show a notification when a new update available [Linux].
[Linux] - This PR adds a feature to notify the user whenever a new update is available.

Fixes: #398.
2018-02-22 16:55:45 +05:30
Priyank P
d48b6ae80d reconnect: Check wheather internet is working before reloading. (#415)
When the online event is triggered check whether the internet is actually working or not.
Commonly on windows, it turns out that internet takes couple of seconds to boot up after
connecting to the internet or in some cases, this might be they have to sign in to internet service
portal in order to access the internet.
2018-02-21 16:56:01 +05:30
Akash Nimare
2d07d40c92 activetab: Fix broken last active tab. (#420)
This fixes an issue where the server tabs were not loading
according to the last active tab. This used to load last server
no matter what the last used server is.

Fixes - #416.
2018-02-18 17:52:26 +05:30
Akash Nimare
55ae71c00c travis: Fix travis not failing on linting errors. 2018-02-15 19:58:08 +05:30
cPhost
0c18bb5226 menu.js: Fix linting error. 2018-02-15 19:55:37 +05:30
Abhigyan Khaund
6d213c86a8 design: Add back button in left-sidebar.
Fixes: #208.
2018-02-14 14:01:35 +05:30
Akash Nimare
0c1cd96ed7 Remove transition property from checkbox input.
That delay can cause the setting button to not change/switch properly.
2018-02-13 17:23:39 +05:30
Priyank P
d77b1b5960 electron-connect: Disable electron-connect when it's run from npm start.
Improves gulp dev script.
2018-02-13 00:07:44 +05:30
Akash Nimare
2c6c0c8c8d Update electron to v1.8.2. 2018-02-11 22:36:32 +05:30
Akash Nimare
84bf59d32f Update package-lock.json files. 2018-02-11 22:08:50 +05:30
Priyank P
256c352fb9 windows: Add tools support with batch scripts.
Add tools support with batch scripts.

Fixes - #397.
2018-02-09 18:19:09 +05:30
Akash Nimare
da28589c17 menu: Add menu-item to reveal app logs in file manager #405. 2018-02-03 02:04:36 +05:30
Akash Nimare
c452ee2ef6 menu: Improve help menu. 2018-02-03 01:37:13 +05:30
Abhigyan Khaund
3582aa4694 menu: Rename Settings to Desktop App Settings. 2018-02-02 17:07:12 +05:30
akashnimare
7f7eee2455 setting-page: design improvemnets. 2018-01-31 21:36:39 +05:30
akashnimare
c716b8f233 notification: Refactor code for bot mention in reply. 2018-01-31 01:34:46 +05:30
Abhigyan Khaund
4a40c75127 notifications: Adds bots mention support to reply option. (#395)
Fixes: #391.
2018-01-31 01:26:11 +05:30
Balaji
239631a2b6 tools: Add scripts for review Pull Requests. (#399)
* tools: Add script to fetch-pull-request.

Improves #397.

* tools: Add script to fetch-rebase-pull-request.

Improves #397.
2018-01-30 17:11:10 +05:30
Abhigyan Khaund
89d1344e2f preference page: Add a Loading indication for new server button. (#401)
Change the text of "Add" button to "Adding..." when a user clicks on Add button for adding new server.

Fixes: #396.
2018-01-29 23:53:10 +05:30
akashnimare
1948ba2cc3 menu: Fix Zoom In shortcut.
This is a temporary fix. Ideally, 'CmdOrCtrl+Plus' works on all
the platforms but because of https://github.com/electron/electron/issues/6731
it converts accelerator to 'Ctrl+Shift+Plus'.
2018-01-27 19:56:27 +05:30
Abhigyan Khaund
b8da7dd6ee gulp-dev: Add nodeman to dev script and fix renderer reload on changes.
Fixes: #368.
2018-01-23 00:29:15 +05:30
akashnimare
4a0efb7301 Code refactoring. 2018-01-23 00:05:15 +05:30
Priyank P
aedd95259d preference: Only toggle the state if element is present.
Fixes #393.
2018-01-22 22:38:01 +05:30
Priyank P
c8d7a79877 design: Improve preference design. 2018-01-17 18:34:24 +05:30
akashnimare
6e6db42b54 v1.8.1 2018-01-17 02:16:04 +05:30
akashnimare
db79284fbb fix: Don't import appId from package file #386. 2018-01-17 01:54:20 +05:30
akashnimare
2434f06655 release: 🎉 new-release v1.8.0. 2018-01-15 20:59:28 +05:30
akashnimare
1d611d3382 setting-page: Update placeholder for adding new server page.
Improves #340.
2018-01-15 20:55:51 +05:30
akashnimare
a746194e9e help-menu: Add a space between app name and version. 2018-01-15 18:45:45 +05:30
akashnimare
7cc13f7a26 Update dependencies.
Updated:
electron - 1.7.10
electron-builder - 19.53.6
electron-updater - 2.18.2
2018-01-15 17:46:33 +05:30
Akash Nimare
6a9bb152a0 Update features section. 2018-01-14 01:11:18 +05:30
Priyank P
8b6dcd355f notification: Add reply option to notifications for macOS.
This PR adds reply option to notifications of macOS using
`node-mac-notifier` and then post the reply for to the webapp.
It also fixes an issue that even though the app is focused the server that sent
the notification did not focus. And it also adds parsing for mentioning. This also
refactors code for notification.

Fixes: #284, #381.
2018-01-14 00:04:34 +05:30
170 changed files with 5441 additions and 940 deletions

View File

@@ -2,8 +2,10 @@ sudo: required
dist: trusty dist: trusty
os: os:
- osx - osx
- linux - linux
osx_image: xcode9.0
addons: addons:
apt: apt:
@@ -12,6 +14,19 @@ addons:
- libxext-dev - libxext-dev
- libxtst-dev - libxtst-dev
- libxkbfile-dev - libxkbfile-dev
artifacts:
paths:
- $(ls ./dist/*.AppImage | tr "\n" ":")
- $(ls ./dist/*.deb | tr "\n" ":")
- $(ls ./dist/*.dmg | tr "\n" ":")
- $(ls ./dist/*.zip | tr "\n" ":")
- $(ls ./dist/*.dmg.blockmap | tr "\n" ":")
- $(ls ./dist/github/*.json | tr "\n" ":")
- $(ls ./dist/github/*.yml | tr "\n" ":")
- $(ls ./dist/*.yml | tr "\n" ":")
- $(ls ./dist/mac/*.yml | tr "\n" ":")
- $(ls ./dist/linux/*.yml | tr "\n" ":")
debug: true
language: node_js language: node_js
node_js: node_js:
@@ -26,9 +41,18 @@ cache:
directories: directories:
- node_modules - node_modules
- app/node_modules - app/node_modules
- ~/.cache
script: script:
- npm run travis - npm run travis
- chmod +x ./scripts/install-release-dependencies.sh
- ./scripts/install-release-dependencies.sh
- npm run dist
- node ./scripts/prepare-artifacts.js
# log out /dist files might be useful to know
# what files are uploaded
- ls dist
notifications: notifications:
webhooks: webhooks:
urls: urls:

View File

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

View File

@@ -11,6 +11,12 @@ function appUpdater() {
return; return;
} }
if (process.platform === 'linux' && !process.env.APPIMAGE) {
const { linuxUpdateNotification } = require('./linuxupdater');
linuxUpdateNotification();
return;
}
// Create Logs directory // Create Logs directory
const LogsDir = `${app.getPath('userData')}/Logs`; const LogsDir = `${app.getPath('userData')}/Logs`;

View File

@@ -166,10 +166,11 @@ app.on('ready', () => {
crashHandler(); crashHandler();
}); });
electron.powerMonitor.on('resume', () => { // Temporarily remove this event
mainWindow.reload(); // electron.powerMonitor.on('resume', () => {
page.send('destroytray'); // mainWindow.reload();
}); // page.send('destroytray');
// });
ipcMain.on('focus-app', () => { ipcMain.on('focus-app', () => {
mainWindow.show(); mainWindow.show();
@@ -221,11 +222,6 @@ app.on('ready', () => {
appMenu.setMenu(props); appMenu.setMenu(props);
}); });
ipcMain.on('register-server-tab-shortcut', (event, index) => {
// Array index == Shown index - 1
page.send('switch-server-tab', index - 1);
});
ipcMain.on('toggleAutoLauncher', (event, AutoLaunchValue) => { ipcMain.on('toggleAutoLauncher', (event, AutoLaunchValue) => {
setAutoLaunch(AutoLaunchValue); setAutoLaunch(AutoLaunchValue);
}); });

42
app/main/linuxupdater.js Normal file
View File

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

View File

@@ -54,7 +54,7 @@ class AppMenu {
role: 'togglefullscreen' role: 'togglefullscreen'
}, { }, {
label: 'Zoom In', label: 'Zoom In',
accelerator: 'CommandOrControl+Plus', accelerator: process.platform === 'darwin' ? 'Command+Plus' : 'Control+=',
click(item, focusedWindow) { click(item, focusedWindow) {
if (focusedWindow) { if (focusedWindow) {
AppMenu.sendAction('zoomIn'); AppMenu.sendAction('zoomIn');
@@ -115,14 +115,27 @@ class AppMenu {
} }
getHelpSubmenu() { getHelpSubmenu() {
return [{ return [
label: `${appName} Website`, {
label: `${appName + ' Desktop-'} v${app.getVersion()}`,
enabled: false
},
{
label: `What's New...`,
click() {
shell.openExternal(`https://github.com/zulip/zulip-electron/releases/tag/v${app.getVersion()}`);
}
},
{
label: `${appName} Help`,
click() { click() {
shell.openExternal('https://zulipchat.com/help/'); shell.openExternal('https://zulipchat.com/help/');
} }
}, { }, {
label: `${appName + 'Desktop'} - ${app.getVersion()}`, label: 'Show App Logs',
enabled: false click() {
shell.openItem(app.getPath('userData'));
}
}, { }, {
label: 'Report an Issue...', label: 'Report an Issue...',
click() { click() {
@@ -150,6 +163,11 @@ class AppMenu {
type: 'separator' type: 'separator'
}); });
for (let i = 0; i < tabs.length; i++) { for (let i = 0; i < tabs.length; i++) {
// Do not add functional tab settings to list of windows in menu bar
if (tabs[i].props.role === 'function' && tabs[i].webview.props.name === 'Settings') {
continue;
}
initialSubmenu.push({ initialSubmenu.push({
label: tabs[i].webview.props.name, label: tabs[i].webview.props.name,
accelerator: tabs[i].props.role === 'function' ? '' : `${ShortcutKey} + ${tabs[i].props.index + 1}`, accelerator: tabs[i].props.role === 'function' ? '' : `${ShortcutKey} + ${tabs[i].props.index + 1}`,
@@ -159,7 +177,7 @@ class AppMenu {
AppMenu.sendAction('switch-server-tab', tabs[i].props.index); AppMenu.sendAction('switch-server-tab', tabs[i].props.index);
} }
}, },
type: 'radio' type: 'checkbox'
}); });
} }
} }
@@ -182,7 +200,7 @@ class AppMenu {
}, { }, {
type: 'separator' type: 'separator'
}, { }, {
label: 'Settings', label: 'Desktop App Settings',
accelerator: 'Cmd+,', accelerator: 'Cmd+,',
click(item, focusedWindow) { click(item, focusedWindow) {
if (focusedWindow) { if (focusedWindow) {
@@ -191,7 +209,7 @@ class AppMenu {
} }
}, { }, {
label: 'Keyboard Shortcuts', label: 'Keyboard Shortcuts',
accelerator: 'Cmd+K', accelerator: 'Cmd+Shift+K',
click(item, focusedWindow) { click(item, focusedWindow) {
if (focusedWindow) { if (focusedWindow) {
AppMenu.sendAction('shortcut'); AppMenu.sendAction('shortcut');
@@ -282,7 +300,7 @@ class AppMenu {
}, { }, {
type: 'separator' type: 'separator'
}, { }, {
label: 'Settings', label: 'Desktop App Settings',
accelerator: 'Ctrl+,', accelerator: 'Ctrl+,',
click(item, focusedWindow) { click(item, focusedWindow) {
if (focusedWindow) { if (focusedWindow) {
@@ -293,7 +311,7 @@ class AppMenu {
type: 'separator' type: 'separator'
}, { }, {
label: 'Keyboard Shortcuts', label: 'Keyboard Shortcuts',
accelerator: 'Ctrl+K', accelerator: 'Ctrl+Shift+K',
click(item, focusedWindow) { click(item, focusedWindow) {
if (focusedWindow) { if (focusedWindow) {
AppMenu.sendAction('shortcut'); AppMenu.sendAction('shortcut');

592
app/package-lock.json generated
View File

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

View File

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

View File

@@ -13,7 +13,7 @@ body {
#content { #content {
display: flex; display: flex;
height: 100%; height: 100%;
background: #eee url(../img/ic_loading.gif) no-repeat; background: #fff url(../img/ic_loading.gif) no-repeat;
background-size: 60px 60px; background-size: 60px 60px;
background-position: center; background-position: center;
} }
@@ -28,6 +28,7 @@ body {
-webkit-app-region: drag; -webkit-app-region: drag;
overflow: hidden; overflow: hidden;
transition: all 0.5s ease; transition: all 0.5s ease;
z-index: 2;
} }
.toggle-sidebar div { .toggle-sidebar div {
@@ -81,11 +82,17 @@ body {
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
} }
#actions-container {
display: flex;
flex-direction: column;
position: fixed;
bottom: 0;
}
.action-button { .action-button {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; padding: 12px;
padding: 10px;
} }
.action-button:hover { .action-button:hover {
@@ -101,6 +108,26 @@ body {
color: #98a9b3; color: #98a9b3;
} }
.action-button.disable {
opacity: 0.6;
}
.action-button.disable:hover {
cursor: not-allowed;
}
.action-button.disable:hover i {
color: #6c8592;
}
.action-button.active {
background-color: rgba(255, 255, 255, 0.25);
}
.action-button.active i {
color: #eee;
}
.tab:first-child { .tab:first-child {
margin-top: 8px; margin-top: 8px;
} }
@@ -222,35 +249,42 @@ body {
} }
webview { webview {
opacity: 1; /* transition: opacity 0.3s ease-in; */
transition: opacity 0.3s ease-in;
flex-grow: 1; flex-grow: 1;
position: absolute;
width: 100%;
height: 100%;
flex-grow: 1;
display: flex;
flex-direction: column;
} }
webview.onload { webview.onload {
transition: opacity 1s cubic-bezier(0.95, 0.05, 0.795, 0.035); transition: opacity 1s cubic-bezier(0.95, 0.05, 0.795, 0.035);
} }
webview.disabled { webview.active {
flex: 0 1; opacity: 1;
height: 0; z-index: 1;
width: 0; visibility: visible;
opacity: 0;
transition: opacity 0.3s ease-out;
} }
webview:focus { webview.disabled {
opacity: 0;
}
webview.focus {
outline: 0px solid transparent; outline: 0px solid transparent;
} }
/* Tooltip styling */ /* Tooltip styling */
#back-tooltip,
#reload-tooltip, #reload-tooltip,
#setting-tooltip { #setting-tooltip {
font-family: sans-serif; font-family: sans-serif;
background: #222c31; background: #222c31;
margin-left: 68px; margin-left: 45px;
padding: 6px 8px; padding: 6px 8px;
position: absolute; position: absolute;
margin-top: 0px; margin-top: 0px;
@@ -262,6 +296,7 @@ webview:focus {
font-size: 14px; font-size: 14px;
} }
#back-tooltip:after,
#reload-tooltip:after, #reload-tooltip:after,
#setting-tooltip:after { #setting-tooltip:after {
content: " "; content: " ";

View File

@@ -12,16 +12,19 @@ body {
} }
kbd { kbd {
padding: 0.3em 0.8em; display: inline-block;
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 3px;
font-size: 15px; font-size: 15px;
font-family: Courier New, Courier, monospace; font-family: Courier New, Courier, monospace;
background-color: #383430;
color: #ededed;
display: inline-block;
margin: 0 0.1em;
font-weight: bold; font-weight: bold;
white-space: nowrap; white-space: nowrap;
background-color: #f7f7f7;
color: #333;
margin: 0 0.1em;
padding: 0.3em 0.8em;
text-shadow: 0 1px 0 #fff;
line-height: 1.4;
} }
table, table,
@@ -38,11 +41,11 @@ table {
} }
table tr:nth-child(even) { table tr:nth-child(even) {
background-color: #f7eee6; background-color: #fafafa;
} }
table tr:nth-child(odd) { table tr:nth-child(odd) {
background-color: #fff8ef; background-color: #fff;
} }
td { td {
@@ -92,11 +95,13 @@ td:nth-child(odd) {
} }
#sidebar { #sidebar {
width: 80px; width: 150px;
padding: 30px; min-width: 100px;
padding: 30px 30px 30px 35px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
font-size: 16px; font-size: 16px;
background: #f2f2f2;
} }
#nav-container { #nav-container {
@@ -263,7 +268,12 @@ img.server-info-icon {
margin: 10px 0 20px 0; margin: 10px 0 20px 0;
background: #fff; background: #fff;
width: 70%; width: 70%;
transition: all 0.2s;
}
.settings-card:hover {
border-left: 8px solid #bcbcbc; border-left: 8px solid #bcbcbc;
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 0px 0px rgba(0, 0, 0, 0.12);
} }
.hidden { .hidden {
@@ -311,7 +321,8 @@ i.open-tab-button {
cursor: pointer; cursor: pointer;
} }
.reset-data-button { .reset-data-button,
.custom-css-button {
display: inline-block; display: inline-block;
border: none; border: none;
padding: 10px; padding: 10px;
@@ -322,19 +333,35 @@ i.open-tab-button {
text-decoration: none; text-decoration: none;
} }
.css-delete-action {
margin-bottom: 10px;
}
.reset-data-button:hover { .reset-data-button:hover {
background-color: #3c9f8d; background-color: #3c9f8d;
color: #fff; color: #fff;
} }
.selected-css-path {
background: #eeeeee;
padding: 10px;
margin-top: 10px;
margin-right: 10px;
display: flex;
width: 90%;
justify-content: space-between;
}
#remove-custom-css {
align-items: flex-end;
}
#server-info-container { #server-info-container {
min-height: calc(100% - 235px); min-height: calc(100% - 260px);
} }
#create-organization-container { #create-organization-container {
font-size: 1.15em; font-size: 1.15em;
position: fixed;
bottom: 15px;
} }
#create-organization-container i { #create-organization-container i {
@@ -390,7 +417,6 @@ input.toggle-round+label:before {
right: 2px; right: 2px;
background-color: #f1f1f1; background-color: #f1f1f1;
border-radius: 25px; border-radius: 25px;
transition: background 0.4s;
} }
input.toggle-round+label:after { input.toggle-round+label:after {
@@ -398,7 +424,6 @@ input.toggle-round+label:after {
height: 25px; height: 25px;
background-color: #fff; background-color: #fff;
border-radius: 100%; border-radius: 100%;
transition: margin 0.4s;
} }
input.toggle-round:checked+label:before { input.toggle-round:checked+label:before {
@@ -408,3 +433,18 @@ input.toggle-round:checked+label:before {
input.toggle-round:checked+label:after { input.toggle-round:checked+label:after {
margin-left: 25px; margin-left: 25px;
} }
/* responsive grid */
@media (max-width: 650px) {
.selected-css-path {
margin-right: 15px;
}
#css-delete-action {
margin-left: 10px;
}
#css-delete-action span {
display: none;
}
}

View File

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

View File

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

View File

@@ -7,7 +7,7 @@ const DomainUtil = require(__dirname + '/../utils/domain-util.js');
const ConfigUtil = require(__dirname + '/../utils/config-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, dialog } = require('electron').remote;
const BaseComponent = require(__dirname + '/../components/base.js'); const BaseComponent = require(__dirname + '/../components/base.js');
@@ -21,11 +21,13 @@ class WebView extends BaseComponent {
this.zoomFactor = 1.0; this.zoomFactor = 1.0;
this.loading = false; this.loading = false;
this.badgeCount = 0; this.badgeCount = 0;
this.customCSS = ConfigUtil.getConfigItem('customCSS');
} }
template() { template() {
return `<webview return `<webview
class="disabled" class="disabled"
data-tab-id="${this.props.tabIndex}"
src="${this.props.url}" src="${this.props.url}"
${this.props.nodeIntegration ? 'nodeIntegration' : ''} ${this.props.nodeIntegration ? 'nodeIntegration' : ''}
disablewebsecurity disablewebsecurity
@@ -68,6 +70,18 @@ class WebView extends BaseComponent {
this.props.onTitleChange(); this.props.onTitleChange();
}); });
this.$el.addEventListener('did-navigate-in-page', event => {
const isSettingPage = event.url.includes('renderer/preference.html');
if (isSettingPage) {
return;
}
this.canGoBackButton();
});
this.$el.addEventListener('did-navigate', () => {
this.canGoBackButton();
});
this.$el.addEventListener('page-favicon-updated', event => { this.$el.addEventListener('page-favicon-updated', event => {
const { favicons } = event; const { favicons } = event;
// This returns a string of favicons URL. If there is a PM counts in unread messages then the URL would be like // This returns a string of favicons URL. If there is a PM counts in unread messages then the URL would be like
@@ -116,6 +130,7 @@ class WebView extends BaseComponent {
} }
this.$el.classList.remove('disabled'); this.$el.classList.remove('disabled');
this.$el.classList.add('active');
setTimeout(() => { setTimeout(() => {
if (this.props.role === 'server') { if (this.props.role === 'server') {
this.$el.classList.remove('onload'); this.$el.classList.remove('onload');
@@ -126,6 +141,21 @@ class WebView extends BaseComponent {
this.props.onTitleChange(); this.props.onTitleChange();
// Injecting preload css in webview to override some css rules // Injecting preload css in webview to override some css rules
this.$el.insertCSS(fs.readFileSync(path.join(__dirname, '/../../css/preload.css'), 'utf8')); this.$el.insertCSS(fs.readFileSync(path.join(__dirname, '/../../css/preload.css'), 'utf8'));
// get customCSS again from config util to avoid warning user again
this.customCSS = ConfigUtil.getConfigItem('customCSS');
if (this.customCSS) {
if (!fs.existsSync(this.customCSS)) {
this.customCSS = null;
ConfigUtil.setConfigItem('customCSS', null);
const errMsg = 'The custom css previously set is deleted!';
dialog.showErrorBox('custom css file deleted!', errMsg);
return;
}
this.$el.insertCSS(fs.readFileSync(path.resolve(__dirname, this.customCSS), 'utf8'));
}
} }
focus() { focus() {
@@ -139,6 +169,7 @@ class WebView extends BaseComponent {
hide() { hide() {
this.$el.classList.add('disabled'); this.$el.classList.add('disabled');
this.$el.classList.remove('active');
} }
load() { load() {
@@ -182,6 +213,15 @@ class WebView extends BaseComponent {
} }
} }
canGoBackButton() {
const $backButton = document.querySelector('#actions-container #back-action');
if (this.$el.canGoBack()) {
$backButton.classList.remove('disable');
} else {
$backButton.classList.add('disable');
}
}
forward() { forward() {
if (this.$el.canGoForward()) { if (this.$el.canGoForward()) {
this.$el.goForward(); this.$el.goForward();

View File

@@ -1,6 +1,7 @@
'use strict'; 'use strict';
const { ipcRenderer, remote } = require('electron'); const { ipcRenderer, remote } = require('electron');
const isDev = require('electron-is-dev');
const { session } = remote; const { session } = remote;
@@ -10,6 +11,7 @@ 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');
const FunctionalTab = require(__dirname + '/js/components/functional-tab.js'); const FunctionalTab = require(__dirname + '/js/components/functional-tab.js');
const ConfigUtil = require(__dirname + '/js/utils/config-util.js'); const ConfigUtil = require(__dirname + '/js/utils/config-util.js');
const ReconnectUtil = require(__dirname + '/js/utils/reconnect-util.js');
class ServerManagerView { class ServerManagerView {
constructor() { constructor() {
@@ -20,11 +22,13 @@ class ServerManagerView {
this.$reloadButton = $actionsContainer.querySelector('#reload-action'); this.$reloadButton = $actionsContainer.querySelector('#reload-action');
this.$settingsButton = $actionsContainer.querySelector('#settings-action'); this.$settingsButton = $actionsContainer.querySelector('#settings-action');
this.$webviewsContainer = document.getElementById('webviews-container'); this.$webviewsContainer = document.getElementById('webviews-container');
this.$backButton = $actionsContainer.querySelector('#back-action');
this.$addServerTooltip = document.getElementById('add-server-tooltip'); this.$addServerTooltip = document.getElementById('add-server-tooltip');
this.$reloadTooltip = $actionsContainer.querySelector('#reload-tooltip'); this.$reloadTooltip = $actionsContainer.querySelector('#reload-tooltip');
this.$settingsTooltip = $actionsContainer.querySelector('#setting-tooltip'); this.$settingsTooltip = $actionsContainer.querySelector('#setting-tooltip');
this.$serverIconTooltip = document.getElementsByClassName('server-tooltip'); this.$serverIconTooltip = document.getElementsByClassName('server-tooltip');
this.$backTooltip = $actionsContainer.querySelector('#back-tooltip');
this.$sidebar = document.getElementById('sidebar'); this.$sidebar = document.getElementById('sidebar');
@@ -35,6 +39,7 @@ class ServerManagerView {
this.activeTabIndex = -1; this.activeTabIndex = -1;
this.tabs = []; this.tabs = [];
this.functionalTabs = {}; this.functionalTabs = {};
this.tabIndex = 0;
} }
init() { init() {
@@ -114,23 +119,28 @@ class ServerManagerView {
} }
// Open last active tab // Open last active tab
this.activateTab(ConfigUtil.getConfigItem('lastActiveTab')); this.activateTab(ConfigUtil.getConfigItem('lastActiveTab'));
// Remove focus from the settings icon at sidebar bottom
this.$settingsButton.classList.remove('active');
} else { } else {
this.openSettings('Servers'); this.openSettings('Servers');
} }
} }
initServer(server, index) { initServer(server, index) {
const tabIndex = this.getTabIndex();
this.tabs.push(new ServerTab({ this.tabs.push(new ServerTab({
role: 'server', role: 'server',
icon: server.icon, icon: server.icon,
$root: this.$tabsContainer, $root: this.$tabsContainer,
onClick: this.activateLastTab.bind(this, index), onClick: this.activateLastTab.bind(this, index),
index, index,
tabIndex,
onHover: this.onHover.bind(this, index, server.alias), onHover: this.onHover.bind(this, index, server.alias),
onHoverOut: this.onHoverOut.bind(this, index), onHoverOut: this.onHoverOut.bind(this, index),
webview: new WebView({ webview: new WebView({
$root: this.$webviewsContainer, $root: this.$webviewsContainer,
index, index,
tabIndex,
url: server.url, url: server.url,
name: server.alias, name: server.alias,
isActive: () => { isActive: () => {
@@ -154,6 +164,9 @@ class ServerManagerView {
this.$settingsButton.addEventListener('click', () => { this.$settingsButton.addEventListener('click', () => {
this.openSettings('General'); this.openSettings('General');
}); });
this.$backButton.addEventListener('click', () => {
this.tabs[this.activeTabIndex].webview.back();
});
const $serverImgs = document.querySelectorAll('.server-icons'); const $serverImgs = document.querySelectorAll('.server-icons');
$serverImgs.forEach($serverImg => { $serverImgs.forEach($serverImg => {
@@ -165,6 +178,13 @@ class ServerManagerView {
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);
this.sidebarHoverEvent(this.$backButton, this.$backTooltip);
}
getTabIndex() {
const currentIndex = this.tabIndex;
this.tabIndex++;
return currentIndex;
} }
sidebarHoverEvent(SidebarButton, SidebarTooltip) { sidebarHoverEvent(SidebarButton, SidebarTooltip) {
@@ -193,16 +213,20 @@ class ServerManagerView {
this.functionalTabs[tabProps.name] = this.tabs.length; this.functionalTabs[tabProps.name] = this.tabs.length;
const tabIndex = this.getTabIndex();
this.tabs.push(new FunctionalTab({ this.tabs.push(new FunctionalTab({
role: 'function', role: 'function',
materialIcon: tabProps.materialIcon, materialIcon: tabProps.materialIcon,
name: tabProps.name,
$root: this.$tabsContainer, $root: this.$tabsContainer,
index: this.functionalTabs[tabProps.name], index: this.functionalTabs[tabProps.name],
tabIndex,
onClick: this.activateTab.bind(this, this.functionalTabs[tabProps.name]), onClick: this.activateTab.bind(this, this.functionalTabs[tabProps.name]),
onDestroy: this.destroyTab.bind(this, tabProps.name, this.functionalTabs[tabProps.name]), onDestroy: this.destroyTab.bind(this, tabProps.name, this.functionalTabs[tabProps.name]),
webview: new WebView({ webview: new WebView({
$root: this.$webviewsContainer, $root: this.$webviewsContainer,
index: this.functionalTabs[tabProps.name], index: this.functionalTabs[tabProps.name],
tabIndex,
url: tabProps.url, url: tabProps.url,
name: tabProps.name, name: tabProps.name,
isActive: () => { isActive: () => {
@@ -214,7 +238,6 @@ class ServerManagerView {
preload: false preload: false
}) })
})); }));
this.activateTab(this.functionalTabs[tabProps.name]); this.activateTab(this.functionalTabs[tabProps.name]);
} }
@@ -224,6 +247,7 @@ class ServerManagerView {
materialIcon: 'settings', materialIcon: 'settings',
url: `file://${__dirname}/preference.html#${nav}` url: `file://${__dirname}/preference.html#${nav}`
}); });
this.$settingsButton.classList.add('active');
this.tabs[this.functionalTabs.Settings].webview.send('switch-settings-nav', nav); this.tabs[this.functionalTabs.Settings].webview.send('switch-settings-nav', nav);
} }
@@ -259,10 +283,19 @@ class ServerManagerView {
if (this.activeTabIndex === index) { if (this.activeTabIndex === index) {
return; return;
} else if (hideOldTab) { } else if (hideOldTab) {
// If old tab is functional tab Settings, remove focus from the settings icon at sidebar bottom
if (this.tabs[this.activeTabIndex].props.role === 'function' && this.tabs[this.activeTabIndex].props.name === 'Settings') {
this.$settingsButton.classList.remove('active');
}
this.tabs[this.activeTabIndex].deactivate(); this.tabs[this.activeTabIndex].deactivate();
} }
} }
try {
this.tabs[index].webview.canGoBackButton();
} catch (err) {
}
this.activeTabIndex = index; this.activeTabIndex = index;
this.tabs[index].activate(); this.tabs[index].activate();
@@ -399,7 +432,7 @@ class ServerManagerView {
}); });
ipcRenderer.on('switch-server-tab', (event, index) => { ipcRenderer.on('switch-server-tab', (event, index) => {
this.activateTab(index); this.activateLastTab(index);
}); });
ipcRenderer.on('reload-proxy', (event, showAlert) => { ipcRenderer.on('reload-proxy', (event, showAlert) => {
@@ -423,6 +456,18 @@ class ServerManagerView {
this.$fullscreenPopup.classList.remove('show'); this.$fullscreenPopup.classList.remove('show');
}); });
ipcRenderer.on('focus-webview-with-id', (event, webviewId) => {
const webviews = document.querySelectorAll('webview');
webviews.forEach(webview => {
const currentId = webview.getWebContents().id;
const tabId = webview.getAttribute('data-tab-id');
const concurrentTab = document.querySelector(`div[data-tab-id="${tabId}"]`);
if (currentId === webviewId) {
concurrentTab.click();
}
});
});
ipcRenderer.on('render-taskbar-icon', (event, messageCount) => { ipcRenderer.on('render-taskbar-icon', (event, messageCount) => {
// Create a canvas from unread messagecounts // Create a canvas from unread messagecounts
function createOverlayIcon(messageCount) { function createOverlayIcon(messageCount) {
@@ -456,9 +501,24 @@ class ServerManagerView {
window.onload = () => { window.onload = () => {
const serverManagerView = new ServerManagerView(); const serverManagerView = new ServerManagerView();
const reconnectUtil = new ReconnectUtil(serverManagerView);
serverManagerView.init(); serverManagerView.init();
window.addEventListener('online', () => { window.addEventListener('online', () => {
serverManagerView.reloadView(); reconnectUtil.pollInternetAndReload();
}); });
window.addEventListener('offline', () => {
reconnectUtil.clearState();
console.log('No internet connection, you are offline.');
});
// only start electron-connect (auto reload on change) when its ran
// from `npm run dev` or `gulp dev` and not from `npm start` when
// app is started `npm start` main process's proces.argv will have
// `--no-electron-connect`
const mainProcessArgv = remote.getGlobal('process').argv;
if (isDev && !mainProcessArgv.includes('--no-electron-connect')) {
const electronConnect = require('electron-connect');
electronConnect.client.create();
}
}; };

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -69,6 +69,24 @@ class GeneralSection extends BaseSection {
<div class="setting-control"></div> <div class="setting-control"></div>
</div> </div>
</div> </div>
<div class="title">Add custom CSS</div>
<div class="settings-card">
<div class="setting-row" id="add-custom-css">
<div class="setting-description">
This will inject the selected css stylesheet in all the added accounts
</div>
<button class="custom-css-button blue">Add</button>
</div>
<div class="setting-row" id="remove-custom-css">
<div class="setting-description">
<div class="selected-css-path" id="custom-css-path">${ConfigUtil.getConfigItem('customCSS')}</div>
</div>
<div class="action red" id="css-delete-action">
<i class="material-icons">indeterminate_check_box</i>
<span>Delete</span>
</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">
<div class="setting-row" id="resetdata-option"> <div class="setting-row" id="resetdata-option">
@@ -93,6 +111,9 @@ class GeneralSection extends BaseSection {
this.showDesktopNotification(); this.showDesktopNotification();
this.enableSpellchecker(); this.enableSpellchecker();
this.minimizeOnStart(); this.minimizeOnStart();
this.addCustomCSS();
this.showCustomCSSPath();
this.removeCustomCSS();
// Platform specific settings // Platform specific settings
// Flashing taskbar on Windows // Flashing taskbar on Windows
@@ -232,6 +253,22 @@ class GeneralSection extends BaseSection {
}); });
} }
customCssDialog() {
const showDialogOptions = {
title: 'Select file',
defaultId: 1,
properties: ['openFile'],
filters: [{ name: 'CSS file', extensions: ['css'] }]
};
dialog.showOpenDialog(showDialogOptions, selectedFile => {
if (selectedFile) {
ConfigUtil.setConfigItem('customCSS', selectedFile[0]);
ipcRenderer.send('forward-message', 'hard-reload');
}
});
}
updateResetDataOption() { updateResetDataOption() {
const resetDataButton = document.querySelector('#resetdata-option .reset-data-button'); const resetDataButton = document.querySelector('#resetdata-option .reset-data-button');
resetDataButton.addEventListener('click', () => { resetDataButton.addEventListener('click', () => {
@@ -251,6 +288,28 @@ class GeneralSection extends BaseSection {
}); });
} }
addCustomCSS() {
const customCSSButton = document.querySelector('#add-custom-css .custom-css-button');
customCSSButton.addEventListener('click', () => {
this.customCssDialog();
});
}
showCustomCSSPath() {
if (!ConfigUtil.getConfigItem('customCSS')) {
const cssPATH = document.getElementById('remove-custom-css');
cssPATH.style.display = 'none';
}
}
removeCustomCSS() {
const removeCSSButton = document.getElementById('css-delete-action');
removeCSSButton.addEventListener('click', () => {
ConfigUtil.setConfigItem('customCSS');
ipcRenderer.send('forward-message', 'hard-reload');
});
}
} }
module.exports = GeneralSection; module.exports = GeneralSection;

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
'use strict'; 'use strict';
const {dialog} = require('electron').remote; const { dialog } = require('electron').remote;
const {ipcRenderer} = require('electron'); const { ipcRenderer } = require('electron');
const BaseComponent = require(__dirname + '/../../components/base.js'); const BaseComponent = require(__dirname + '/../../components/base.js');
const DomainUtil = require(__dirname + '/../../utils/domain-util.js'); const DomainUtil = require(__dirname + '/../../utils/domain-util.js');
@@ -55,7 +55,7 @@ class ServerInfoForm extends BaseComponent {
type: 'warning', type: 'warning',
buttons: ['YES', 'NO'], buttons: ['YES', 'NO'],
defaultId: 0, defaultId: 0,
message: 'Are you sure you want to delete this server?' message: 'Are you sure you want to disconnect this organization?'
}, response => { }, response => {
if (response === 0) { if (response === 0) {
DomainUtil.removeDomain(this.props.index); DomainUtil.removeDomain(this.props.index);

View File

@@ -40,7 +40,7 @@ class ServersSection extends BaseSection {
this.$serverInfoContainer.innerHTML = servers.length ? '' : ''; 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 organizations'; this.$existingServers.innerHTML = servers.length === 0 ? '' : 'Connected organizations';
this.initNewServerForm(); this.initNewServerForm();
this.$createOrganizationContainer = document.getElementById('create-organization-container'); this.$createOrganizationContainer = document.getElementById('create-organization-container');

View File

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

View File

@@ -3,111 +3,105 @@ const path = require('path');
const electron = require('electron'); const electron = require('electron');
const { ipcRenderer, remote } = electron; const {ipcRenderer, remote} = electron;
const { Tray, Menu, BrowserWindow } = remote; const {Tray, Menu, nativeImage, BrowserWindow} = remote;
const APP_ICON = path.join(__dirname, '../../resources/', 'f'); const APP_ICON = path.join(__dirname, '../../resources/tray', 'tray');
const ConfigUtil = require(__dirname + '/utils/config-util.js'); const ConfigUtil = require(__dirname + '/utils/config-util.js');
const iconPath = unreadCount => { const iconPath = () => {
if (process.platform === 'linux') { if (process.platform === 'linux') {
return APP_ICON + 'linux.png'; return APP_ICON + 'linux.png';
} }
if (!unreadCount) { return APP_ICON + (process.platform === 'win32' ? 'win.ico' : 'osx.png');
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(() => iconPath(arg)) .then(() => renderCanvas(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];
@@ -187,17 +181,17 @@ 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 === 'darwin' || process.platform === 'win32') { if (process.platform === 'linux' || 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(iconPath(arg)); window.tray.setImage(image);
window.tray.setToolTip(arg + ' unread messages'); window.tray.setToolTip(arg + ' unread messages');
// }); });
} }
} }
}); });
@@ -214,11 +208,11 @@ function toggleTray() {
} else { } else {
state = true; state = true;
createTray(); createTray();
if (process.platform === 'darwin' || process.platform === 'win32') { if (process.platform === 'linux' || process.platform === 'win32') {
// renderNativeImage(unread).then(image => { renderNativeImage(unread).then(image => {
window.tray.setImage(iconPath()); window.tray.setImage(image);
window.tray.setToolTip(unread + ' unread messages'); window.tray.setToolTip(unread + ' unread messages');
// }); });
} }
ConfigUtil.setConfigItem('trayIcon', true); ConfigUtil.setConfigItem('trayIcon', true);
} }

View File

@@ -99,8 +99,7 @@ class DomainUtil {
checkDomain(domain, silent = false) { checkDomain(domain, silent = false) {
if (!silent && this.duplicateDomain(domain)) { if (!silent && this.duplicateDomain(domain)) {
// Do not check duplicate in silent mode // Do not check duplicate in silent mode
alert('This server has been added.'); return Promise.reject('This server has been added.');
return;
} }
domain = this.formatUrl(domain); domain = this.formatUrl(domain);

View File

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

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