mirror of
https://github.com/zulip/zulip-desktop.git
synced 2025-10-24 00:23:36 +00:00
Compare commits
158 Commits
v2.3.4-bet
...
v3.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f74fea0f3 | ||
|
|
405d938223 | ||
|
|
c6b7e9a9ee | ||
|
|
b4073ba7ac | ||
|
|
b504f31c26 | ||
|
|
f71660b3f0 | ||
|
|
5d0516887d | ||
|
|
0dd86f0814 | ||
|
|
eaecc92055 | ||
|
|
4f65c36a72 | ||
|
|
2380b650c9 | ||
|
|
1d713f1df2 | ||
|
|
7fa9c291cb | ||
|
|
8a40e36a63 | ||
|
|
3456720135 | ||
|
|
43840a5245 | ||
|
|
d99c29301e | ||
|
|
6e760973ff | ||
|
|
88c64e9dd6 | ||
|
|
e9db11c156 | ||
|
|
8e8de212d8 | ||
|
|
0e0d7e7c4a | ||
|
|
6b98a49245 | ||
|
|
8e5c326d74 | ||
|
|
862e9e2c8c | ||
|
|
8a0b047d8f | ||
|
|
4a833ef603 | ||
|
|
fefb7247d8 | ||
|
|
54a6903236 | ||
|
|
92ef1bd532 | ||
|
|
bf2e04b3bb | ||
|
|
3e389ea5db | ||
|
|
8edff28daa | ||
|
|
ef6abbf49e | ||
|
|
0f638dd4b5 | ||
|
|
92f0e46927 | ||
|
|
a0dd3832c2 | ||
|
|
a2e84595c9 | ||
|
|
9e33ebae62 | ||
|
|
9bdc5dd9e4 | ||
|
|
ac89ac1cb4 | ||
|
|
31da1131a4 | ||
|
|
4980c71e5a | ||
|
|
3e73511357 | ||
|
|
5f23c8570b | ||
|
|
12f79e18a1 | ||
|
|
ad7f760444 | ||
|
|
7314c1f1dd | ||
|
|
46c3d352a1 | ||
|
|
cef8eadc8f | ||
|
|
d3f742719c | ||
|
|
171d88755c | ||
|
|
09ac1bd338 | ||
|
|
48dd12b738 | ||
|
|
61bbd1cf22 | ||
|
|
bf722da390 | ||
|
|
c24f5b3e45 | ||
|
|
3d0f4d88af | ||
|
|
a2f412c3de | ||
|
|
08559c2f4a | ||
|
|
1f4509a6d2 | ||
|
|
06c8b2e8a2 | ||
|
|
2aa15ee11a | ||
|
|
3c676672ec | ||
|
|
c937317ecf | ||
|
|
44dceda50e | ||
|
|
e1407cb6f7 | ||
|
|
6899a6bc20 | ||
|
|
925fec71d5 | ||
|
|
f847c565f9 | ||
|
|
c2a380d308 | ||
|
|
ac6b206583 | ||
|
|
988405c665 | ||
|
|
a209804692 | ||
|
|
95c4df1898 | ||
|
|
1048c91d9e | ||
|
|
d723c5cd1c | ||
|
|
dcc7cd118c | ||
|
|
d2649dd5d7 | ||
|
|
25e6f6d482 | ||
|
|
bd805b8324 | ||
|
|
e1d98e0d8e | ||
|
|
89e5d2892b | ||
|
|
ab28b56377 | ||
|
|
71aa7e28ba | ||
|
|
ba2bfba459 | ||
|
|
d181b0c2e5 | ||
|
|
bda5c62928 | ||
|
|
b2b4fd1003 | ||
|
|
19770f5fbf | ||
|
|
4592396c4b | ||
|
|
19d3c0f5b5 | ||
|
|
af87789c7f | ||
|
|
36e710dbfe | ||
|
|
a6e1c93d81 | ||
|
|
684d88dd7f | ||
|
|
45aedbd9db | ||
|
|
6f411a819c | ||
|
|
097738332b | ||
|
|
b777f8a50f | ||
|
|
50f06239bb | ||
|
|
94cbc786d6 | ||
|
|
75abf38515 | ||
|
|
c1af019182 | ||
|
|
1ea8aea22a | ||
|
|
a7e418885a | ||
|
|
0ac823c85b | ||
|
|
1096188ddf | ||
|
|
8d0b68316c | ||
|
|
c45ad7096f | ||
|
|
5ca7937e52 | ||
|
|
481b99a2ab | ||
|
|
7eb4adf722 | ||
|
|
4d21a8f8d8 | ||
|
|
94270af265 | ||
|
|
177bff6113 | ||
|
|
b4fa4cacb6 | ||
|
|
06abf6716a | ||
|
|
0ad4ea4fdf | ||
|
|
2ece432d19 | ||
|
|
10cd7850ca | ||
|
|
86e3d06e52 | ||
|
|
6ebc9acbf4 | ||
|
|
278dc686e1 | ||
|
|
cec98c030e | ||
|
|
f10d6eb115 | ||
|
|
8c5e928e9c | ||
|
|
8d142c8c3f | ||
|
|
a09769c560 | ||
|
|
bb99015fd2 | ||
|
|
24f5c9b226 | ||
|
|
dc6c4be5fe | ||
|
|
50647e330b | ||
|
|
73dc3db436 | ||
|
|
09cf21bf49 | ||
|
|
c30d0cc77b | ||
|
|
872ad4d3e7 | ||
|
|
6fd9e1be8b | ||
|
|
76c7f24161 | ||
|
|
f9c270492c | ||
|
|
371c580934 | ||
|
|
3eec4c2209 | ||
|
|
debbfb6b7d | ||
|
|
8bd1492586 | ||
|
|
1115c6d5c3 | ||
|
|
9ba279213c | ||
|
|
89c35cb1d4 | ||
|
|
21d6eb52c5 | ||
|
|
aa1538837b | ||
|
|
ea103380b6 | ||
|
|
60d10d88d1 | ||
|
|
124a842bbd | ||
|
|
7130103999 | ||
|
|
26a144e1c2 | ||
|
|
a5c1ae8726 | ||
|
|
ed5096840d | ||
|
|
3f6d256910 | ||
|
|
28421992ba |
17
.gitattributes
vendored
17
.gitattributes
vendored
@@ -1 +1,16 @@
|
||||
* text=auto
|
||||
* text=auto eol=lf
|
||||
|
||||
package-lock.json binary
|
||||
app/package-lock.json binary
|
||||
*.gif binary
|
||||
*.jpg binary
|
||||
*.jpeg binary
|
||||
*.eot binary
|
||||
*.woff binary
|
||||
*.woff2 binary
|
||||
*.svg binary
|
||||
*.ttf binary
|
||||
*.png binary
|
||||
*.otf binary
|
||||
*.tif binary
|
||||
*.ogg binary
|
||||
|
||||
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@@ -2,7 +2,7 @@
|
||||
<!-- Please Include: -->
|
||||
- **Operating System**:
|
||||
- [ ] Windows
|
||||
- [ ] Linux/Ubutnu
|
||||
- [ ] Linux/Ubuntu
|
||||
- [ ] macOS
|
||||
- **Clear steps to reproduce the issue**:
|
||||
- **Relevant error messages and/or screenshots**:
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -33,6 +33,6 @@ yarn-error.log*
|
||||
config.gypi
|
||||
|
||||
# Test generated files
|
||||
tests/package.json
|
||||
# tests/package.json
|
||||
|
||||
.python-version
|
||||
|
||||
25
.htmlhintrc
Normal file
25
.htmlhintrc
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"tagname-lowercase": true,
|
||||
"attr-lowercase": true,
|
||||
"attr-value-double-quotes": true,
|
||||
"attr-value-not-empty": false,
|
||||
"attr-no-duplication": true,
|
||||
"doctype-first": true,
|
||||
"tag-pair": true,
|
||||
"empty-tag-not-self-closed": true,
|
||||
"spec-char-escape": true,
|
||||
"id-unique": true,
|
||||
"src-not-empty": true,
|
||||
"title-require": true,
|
||||
"alt-require": false,
|
||||
"doctype-html5": true,
|
||||
"id-class-value": "dash",
|
||||
"style-disabled": false,
|
||||
"inline-style-disabled": false,
|
||||
"inline-script-disabled": false,
|
||||
"space-tab-mixed-disabled": "space4",
|
||||
"id-class-ad-disabled": false,
|
||||
"href-abs-or-rel": false,
|
||||
"attr-unsafe-chars": true,
|
||||
"head-script-disabled": true
|
||||
}
|
||||
67
.stylelintrc
Normal file
67
.stylelintrc
Normal file
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"rules": {
|
||||
# Stylistic rules for CSS.
|
||||
"function-comma-space-after": "always",
|
||||
"function-comma-space-before": "never",
|
||||
"function-max-empty-lines": 0,
|
||||
"function-whitespace-after": "always",
|
||||
|
||||
"value-keyword-case": "lower",
|
||||
"value-list-comma-newline-after": "always-multi-line",
|
||||
"value-list-comma-space-after": "always-single-line",
|
||||
"value-list-comma-space-before": "never",
|
||||
"value-list-max-empty-lines": 0,
|
||||
|
||||
"unit-case": "lower",
|
||||
"property-case": "lower",
|
||||
"color-hex-case": "lower",
|
||||
|
||||
"declaration-bang-space-before": "always",
|
||||
"declaration-colon-newline-after": "always-multi-line",
|
||||
"declaration-colon-space-after": "always-single-line",
|
||||
"declaration-colon-space-before": "never",
|
||||
"declaration-block-semicolon-newline-after": "always",
|
||||
"declaration-block-semicolon-space-before": "never",
|
||||
"declaration-block-trailing-semicolon": "always",
|
||||
|
||||
"block-closing-brace-empty-line-before": "never",
|
||||
"block-closing-brace-newline-after": "always",
|
||||
"block-closing-brace-newline-before": "always",
|
||||
"block-opening-brace-newline-after": "always",
|
||||
"block-opening-brace-space-before": "always",
|
||||
|
||||
"selector-attribute-brackets-space-inside": "never",
|
||||
"selector-attribute-operator-space-after": "never",
|
||||
"selector-attribute-operator-space-before": "never",
|
||||
"selector-combinator-space-after": "always",
|
||||
"selector-combinator-space-before": "always",
|
||||
"selector-descendant-combinator-no-non-space": true,
|
||||
"selector-pseudo-class-parentheses-space-inside": "never",
|
||||
"selector-pseudo-element-case": "lower",
|
||||
"selector-pseudo-element-colon-notation": "double",
|
||||
"selector-type-case": "lower",
|
||||
"selector-list-comma-newline-after": "always",
|
||||
"selector-list-comma-space-before": "never",
|
||||
|
||||
"media-feature-colon-space-after": "always",
|
||||
"media-feature-colon-space-before": "never",
|
||||
"media-feature-name-case": "lower",
|
||||
"media-feature-parentheses-space-inside": "never",
|
||||
"media-feature-range-operator-space-after": "always",
|
||||
"media-feature-range-operator-space-before": "always",
|
||||
"media-query-list-comma-newline-after": "always",
|
||||
"media-query-list-comma-space-before": "never",
|
||||
|
||||
"at-rule-name-case": "lower",
|
||||
"at-rule-name-space-after": "always",
|
||||
"at-rule-semicolon-newline-after": "always",
|
||||
"at-rule-semicolon-space-before": "never",
|
||||
|
||||
"comment-whitespace-inside": "always",
|
||||
"indentation": 4,
|
||||
|
||||
# Limit language features
|
||||
"color-no-hex": true,
|
||||
"color-named": "never",
|
||||
}
|
||||
}
|
||||
@@ -2,20 +2,20 @@
|
||||
|
||||
Thanks for taking the time to contribute!
|
||||
|
||||
The following is a set of guidelines for contributing to Zulip Electron Desktop Client. These are just guidelines, not rules, so use your best judgement and feel free to propose changes to this document in a pull request.
|
||||
The following is a set of guidelines for contributing to Zulip's desktop Client. These are just guidelines, not rules, so use your best judgement and feel free to propose changes to this document in a pull request.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Zulip-Desktop app is built on top of [Electron](http://electron.atom.io/). If you are new to Electron, please head over to [this](http://jlord.us/essential-electron/) great article.
|
||||
Zulip-Desktop app is built on top of [Electron](http://electron.atom.io/). If you are new to Electron, please head over to [this](https://jlord.dev/blog/essential-electron) great article.
|
||||
|
||||
## Community
|
||||
|
||||
* The whole Zulip documentation, such as setting up a development environment, setting up with the Zulip webapp project, and testing, can be read [here](https://zulip.readthedocs.io).
|
||||
|
||||
* If you have any questions regarding zulip-electron, open an [issue](https://github.com/zulip/zulip-electron/issues/new/) or ask it on [chat.zulip.org](https://chat.zulip.org/#narrow/stream/16-desktop).
|
||||
* If you have any questions regarding zulip-desktop, open an [issue](https://github.com/zulip/zulip-desktop/issues/new/) or ask it on [chat.zulip.org](https://chat.zulip.org/#narrow/stream/16-desktop).
|
||||
|
||||
## Issue
|
||||
Ensure the bug was not already reported by searching on GitHub under [issues](https://github.com/zulip/zulip-electron/issues). If you're unable to find an open issue addressing the bug, open a [new issue](https://github.com/zulip/zulip-electron/issues/new).
|
||||
Ensure the bug was not already reported by searching on GitHub under [issues](https://github.com/zulip/zulip-desktop/issues). If you're unable to find an open issue addressing the bug, open a [new issue](https://github.com/zulip/zulip-desktop/issues/new).
|
||||
|
||||
The [zulipbot](https://github.com/zulip/zulipbot) helps to claim an issue by commenting the following in the comment section: "**@zulipbot** claim". **@zulipbot** will assign you to the issue and label the issue as **in progress**. For more details, check out [**@zulipbot**](https://github.com/zulip/zulipbot).
|
||||
|
||||
@@ -38,7 +38,7 @@ For example:
|
||||
|
||||
|
||||
## Pull Requests
|
||||
Pull Requests are always welcome.
|
||||
Pull Requests are always welcome.
|
||||
|
||||
1. When you edit the code, please run `npm run test` to check the formatting of your code before you `git commit`.
|
||||
2. Ensure the PR description clearly describes the problem and solution. It should include:
|
||||
|
||||
29
README.md
29
README.md
@@ -1,32 +1,27 @@
|
||||
# Zulip Desktop Client
|
||||
[](https://travis-ci.org/zulip/zulip-electron)
|
||||
[](https://ci.appveyor.com/project/akashnimare/zulip-electron/branch/master)
|
||||
[](https://travis-ci.org/zulip/zulip-desktop)
|
||||
[](https://ci.appveyor.com/project/zulip/zulip-desktop/branch/master)
|
||||
[](https://github.com/sindresorhus/xo)
|
||||
[](https://chat.zulip.org)
|
||||
|
||||
Desktop client for Zulip. Available for Mac, Linux and Windows.
|
||||
Desktop client for Zulip. Available for Mac, Linux, and Windows.
|
||||
|
||||
<img src="http://i.imgur.com/ChzTq4F.png"/>
|
||||
|
||||
# Download
|
||||
Please see [installation guide](https://zulipchat.com/help/desktop-app-install-guide).
|
||||
Please see the [installation guide](https://zulipchat.com/help/desktop-app-install-guide).
|
||||
|
||||
# Features
|
||||
* Sign in to multiple teams
|
||||
* Desktop Notifications with inline reply support
|
||||
* Multilanguage SpellChecker
|
||||
* OSX/Win/Linux installers
|
||||
* Automatic Updates (macOS/Windows/Linux)
|
||||
* Keyboard shortcuts
|
||||
|
||||
# Development
|
||||
Please see our [development guide](./development.md) to get started and run app locally.
|
||||
* Sign in to multiple organizations
|
||||
* Desktop notifications with inline reply
|
||||
* Tray/dock integration
|
||||
* Multi-language spell checker
|
||||
* Automatic updates
|
||||
|
||||
# Contribute
|
||||
|
||||
If you want to contribute please make sure to read [our documentation about contributing](./CONTRIBUTING.md) first.
|
||||
|
||||
* [Issue Tracker](https://github.com/zulip/zulip-electron/issues)
|
||||
* [Source Code](https://github.com/zulip/zulip-electron/)
|
||||
First, join us on the [Zulip community server](https://zulip.readthedocs.io/en/latest/contributing/chat-zulip-org.html)!
|
||||
Also see our [contribution guidelines](./CONTRIBUTING.md) and our [development guide](./development.md).
|
||||
|
||||
# License
|
||||
Released under the [Apache-2.0](./LICENSE) license.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
'use strict';
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const electron = require('electron');
|
||||
const windowStateKeeper = require('electron-window-state');
|
||||
const isDev = require('electron-is-dev');
|
||||
@@ -30,18 +32,19 @@ let isQuitting = false;
|
||||
// Load this url in main window
|
||||
const mainURL = 'file://' + path.join(__dirname, '../renderer', 'main.html');
|
||||
|
||||
const isAlreadyRunning = app.makeSingleInstance(() => {
|
||||
if (mainWindow) {
|
||||
if (mainWindow.isMinimized()) {
|
||||
mainWindow.restore();
|
||||
const singleInstanceLock = app.requestSingleInstanceLock();
|
||||
if (singleInstanceLock) {
|
||||
app.on('second-instance', () => {
|
||||
if (mainWindow) {
|
||||
if (mainWindow.isMinimized()) {
|
||||
mainWindow.restore();
|
||||
}
|
||||
|
||||
mainWindow.show();
|
||||
}
|
||||
|
||||
mainWindow.show();
|
||||
}
|
||||
});
|
||||
|
||||
if (isAlreadyRunning) {
|
||||
return app.quit();
|
||||
});
|
||||
} else {
|
||||
app.quit();
|
||||
}
|
||||
|
||||
const APP_ICON = path.join(__dirname, '../resources', 'Icon');
|
||||
@@ -73,7 +76,8 @@ function createMainWindow() {
|
||||
minHeight: 400,
|
||||
webPreferences: {
|
||||
plugins: true,
|
||||
nodeIntegration: true
|
||||
nodeIntegration: true,
|
||||
partition: 'persist:webviewsession'
|
||||
},
|
||||
show: false
|
||||
});
|
||||
@@ -82,14 +86,6 @@ function createMainWindow() {
|
||||
win.webContents.send('focus');
|
||||
});
|
||||
|
||||
win.once('ready-to-show', () => {
|
||||
if (ConfigUtil.getConfigItem('startMinimized')) {
|
||||
win.minimize();
|
||||
} else {
|
||||
win.show();
|
||||
}
|
||||
});
|
||||
|
||||
win.loadURL(mainURL);
|
||||
|
||||
// Keep the app running in background on close event
|
||||
@@ -155,8 +151,18 @@ app.on('ready', () => {
|
||||
});
|
||||
mainWindow = createMainWindow();
|
||||
|
||||
// Auto-hide menu bar on Windows + Linux
|
||||
if (process.platform !== 'darwin') {
|
||||
const shouldHideMenu = ConfigUtil.getConfigItem('autoHideMenubar') || false;
|
||||
mainWindow.setAutoHideMenuBar(shouldHideMenu);
|
||||
mainWindow.setMenuBarVisibility(!shouldHideMenu);
|
||||
}
|
||||
|
||||
// Initialize sentry for main process
|
||||
sentryInit();
|
||||
const errorReporting = ConfigUtil.getConfigItem('errorReporting');
|
||||
if (errorReporting) {
|
||||
sentryInit();
|
||||
}
|
||||
|
||||
const isSystemProxy = ConfigUtil.getConfigItem('useSystemProxy');
|
||||
|
||||
@@ -168,14 +174,14 @@ app.on('ready', () => {
|
||||
|
||||
page.on('dom-ready', () => {
|
||||
if (ConfigUtil.getConfigItem('startMinimized')) {
|
||||
mainWindow.minimize();
|
||||
mainWindow.hide();
|
||||
} else {
|
||||
mainWindow.show();
|
||||
}
|
||||
});
|
||||
|
||||
page.once('did-frame-finish-load', () => {
|
||||
// Initate auto-updates on MacOS and Windows
|
||||
// Initiate auto-updates on MacOS and Windows
|
||||
if (ConfigUtil.getConfigItem('autoUpdate')) {
|
||||
appUpdater();
|
||||
}
|
||||
@@ -195,6 +201,32 @@ app.on('ready', () => {
|
||||
app.quit();
|
||||
});
|
||||
|
||||
// Code to show pdf in a new BrowserWindow (currently commented out due to bug-upstream)
|
||||
// ipcMain.on('pdf-view', (event, url) => {
|
||||
// // Paddings for pdfWindow so that it fits into the main browserWindow
|
||||
// const paddingWidth = 55;
|
||||
// const paddingHeight = 22;
|
||||
|
||||
// // Get the config of main browserWindow
|
||||
// const mainWindowState = global.mainWindowState;
|
||||
|
||||
// // Window to view the pdf file
|
||||
// const pdfWindow = new electron.BrowserWindow({
|
||||
// x: mainWindowState.x + paddingWidth,
|
||||
// y: mainWindowState.y + paddingHeight,
|
||||
// width: mainWindowState.width - paddingWidth,
|
||||
// height: mainWindowState.height - paddingHeight,
|
||||
// webPreferences: {
|
||||
// plugins: true,
|
||||
// partition: 'persist:webviewsession'
|
||||
// }
|
||||
// });
|
||||
// pdfWindow.loadURL(url);
|
||||
|
||||
// // We don't want to have the menu bar in pdf window
|
||||
// pdfWindow.setMenu(null);
|
||||
// });
|
||||
|
||||
// Reload full app not just webview, useful in debugging
|
||||
ipcMain.on('reload-full-app', () => {
|
||||
mainWindow.reload();
|
||||
@@ -208,10 +240,10 @@ app.on('ready', () => {
|
||||
});
|
||||
|
||||
ipcMain.on('toggle-app', () => {
|
||||
if (mainWindow.isVisible()) {
|
||||
mainWindow.hide();
|
||||
} else {
|
||||
if (!mainWindow.isVisible() || mainWindow.isMinimized()) {
|
||||
mainWindow.show();
|
||||
} else {
|
||||
mainWindow.hide();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -219,6 +251,11 @@ app.on('ready', () => {
|
||||
BadgeSettings.updateBadge(badgeCount, mainWindow);
|
||||
});
|
||||
|
||||
ipcMain.on('toggle-menubar', (event, showMenubar) => {
|
||||
mainWindow.setAutoHideMenuBar(showMenubar);
|
||||
mainWindow.setMenuBarVisibility(!showMenubar);
|
||||
});
|
||||
|
||||
ipcMain.on('update-badge', (event, messageCount) => {
|
||||
badgeCount = messageCount;
|
||||
BadgeSettings.updateBadge(badgeCount, mainWindow);
|
||||
@@ -235,6 +272,10 @@ app.on('ready', () => {
|
||||
|
||||
ipcMain.on('update-menu', (event, props) => {
|
||||
appMenu.setMenu(props);
|
||||
const activeTab = props.tabs[props.activeTabIndex];
|
||||
if (activeTab) {
|
||||
mainWindow.setTitle(`Zulip - ${activeTab.webview.props.name}`);
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on('toggleAutoLauncher', (event, AutoLaunchValue) => {
|
||||
@@ -245,10 +286,29 @@ app.on('ready', () => {
|
||||
page.downloadURL(url);
|
||||
page.session.once('will-download', (event, item) => {
|
||||
const filePath = path.join(downloadPath, item.getFilename());
|
||||
item.setSavePath(filePath);
|
||||
|
||||
const getTimeStamp = () => {
|
||||
const date = new Date();
|
||||
return date.getTime();
|
||||
};
|
||||
|
||||
const formatFile = filePath => {
|
||||
const fileExtension = path.extname(filePath);
|
||||
const baseName = path.basename(filePath, fileExtension);
|
||||
return `${baseName}-${getTimeStamp()}${fileExtension}`;
|
||||
};
|
||||
|
||||
// Update the name and path of the file if it already exists
|
||||
|
||||
const updatedFilePath = path.join(downloadPath, formatFile(filePath));
|
||||
|
||||
const setFilePath = fs.existsSync(filePath) ? updatedFilePath : filePath;
|
||||
|
||||
item.setSavePath(setFilePath);
|
||||
|
||||
item.on('updated', (event, state) => {
|
||||
switch (state) {
|
||||
case 'interrupted' : {
|
||||
case 'interrupted': {
|
||||
// Can interrupted to due to network error, cancel download then
|
||||
console.log('Download interrupted, cancelling and fallback to dialog download.');
|
||||
item.cancel();
|
||||
@@ -258,7 +318,7 @@ app.on('ready', () => {
|
||||
if (item.isPaused()) {
|
||||
item.cancel();
|
||||
}
|
||||
// This event can also be used to show progres in percentage in future.
|
||||
// This event can also be used to show progress in percentage in future.
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -267,8 +327,9 @@ app.on('ready', () => {
|
||||
}
|
||||
});
|
||||
item.once('done', (event, state) => {
|
||||
const getFileName = fs.existsSync(filePath) ? formatFile(filePath) : item.getFilename();
|
||||
if (state === 'completed') {
|
||||
page.send('downloadFileCompleted', item.getSavePath(), item.getFilename());
|
||||
page.send('downloadFileCompleted', item.getSavePath(), getFileName);
|
||||
} else {
|
||||
console.log('Download failed state: ', state);
|
||||
page.send('downloadFileFailed');
|
||||
@@ -278,6 +339,20 @@ app.on('ready', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
ipcMain.on('realm-name-changed', (event, serverURL, realmName) => {
|
||||
page.send('update-realm-name', serverURL, realmName);
|
||||
});
|
||||
|
||||
ipcMain.on('realm-icon-changed', (event, serverURL, iconURL) => {
|
||||
page.send('update-realm-icon', serverURL, iconURL);
|
||||
});
|
||||
|
||||
// Using event.sender.send instead of page.send here to
|
||||
// make sure the value of errorReporting is sent only once on load.
|
||||
ipcMain.on('error-reporting', event => {
|
||||
event.sender.send('error-reporting-val', errorReporting);
|
||||
});
|
||||
});
|
||||
|
||||
app.on('before-quit', () => {
|
||||
|
||||
@@ -4,6 +4,7 @@ const { Notification } = require('electron');
|
||||
const request = require('request');
|
||||
const semver = require('semver');
|
||||
const ConfigUtil = require('../renderer/js/utils/config-util');
|
||||
const ProxyUtil = require('../renderer/js/utils/proxy-util');
|
||||
const LinuxUpdateUtil = require('../renderer/js/utils/linux-update-util');
|
||||
const Logger = require('../renderer/js/utils/logger-util');
|
||||
|
||||
@@ -13,12 +14,15 @@ const logger = new Logger({
|
||||
});
|
||||
|
||||
function linuxUpdateNotification() {
|
||||
let url = 'https://api.github.com/repos/zulip/zulip-electron/releases';
|
||||
let url = 'https://api.github.com/repos/zulip/zulip-desktop/releases';
|
||||
url = ConfigUtil.getConfigItem('betaUpdate') ? url : url + '/latest';
|
||||
const proxyEnabled = ConfigUtil.getConfigItem('useManualProxy') || ConfigUtil.getConfigItem('useSystemProxy');
|
||||
|
||||
const options = {
|
||||
url,
|
||||
headers: {'User-Agent': 'request'}
|
||||
headers: {'User-Agent': 'request'},
|
||||
proxy: proxyEnabled ? ProxyUtil.getProxy(url) : '',
|
||||
ecdhCurve: 'auto'
|
||||
};
|
||||
|
||||
request(options, (error, response, body) => {
|
||||
|
||||
269
app/main/menu.js
269
app/main/menu.js
@@ -39,6 +39,65 @@ class AppMenu {
|
||||
}];
|
||||
}
|
||||
|
||||
getToolsSubmenu() {
|
||||
return [{
|
||||
label: `Check for Updates`,
|
||||
click() {
|
||||
AppMenu.checkForUpdate();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: `Release Notes`,
|
||||
click() {
|
||||
shell.openExternal(`https://github.com/zulip/zulip-desktop/releases/tag/v${app.getVersion()}`);
|
||||
}
|
||||
}, {
|
||||
type: 'separator'
|
||||
}, {
|
||||
label: 'Factory Reset',
|
||||
accelerator: process.platform === 'darwin' ? 'Command+Shift+D' : 'Ctrl+Shift+D',
|
||||
click() {
|
||||
AppMenu.resetAppSettings();
|
||||
}
|
||||
}, {
|
||||
label: 'Download App Logs',
|
||||
click() {
|
||||
const zip = new AdmZip();
|
||||
let date = new Date();
|
||||
date = date.toLocaleDateString().replace(/\//g, '-');
|
||||
|
||||
// Create a zip file of all the logs and config data
|
||||
zip.addLocalFolder(`${app.getPath('appData')}/${appName}/Logs`);
|
||||
zip.addLocalFolder(`${app.getPath('appData')}/${appName}/config`);
|
||||
|
||||
// Put the log file in downloads folder
|
||||
const logFilePath = `${app.getPath('downloads')}/Zulip-logs-${date}.zip`;
|
||||
zip.writeZip(logFilePath);
|
||||
|
||||
// Open and select the log file
|
||||
shell.showItemInFolder(logFilePath);
|
||||
}
|
||||
}, {
|
||||
type: 'separator'
|
||||
}, {
|
||||
label: 'Toggle DevTools for Zulip App',
|
||||
accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
focusedWindow.webContents.toggleDevTools();
|
||||
}
|
||||
}
|
||||
}, {
|
||||
label: 'Toggle DevTools for Active Tab',
|
||||
accelerator: process.platform === 'darwin' ? 'Alt+Command+U' : 'Ctrl+Shift+U',
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
AppMenu.sendAction('tab-devtools');
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
getViewSubmenu() {
|
||||
return [{
|
||||
label: 'Reload',
|
||||
@@ -103,62 +162,30 @@ class AppMenu {
|
||||
ConfigUtil.setConfigItem('showSidebar', newValue);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
label: 'Toggle DevTools for Zulip App',
|
||||
accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
focusedWindow.webContents.toggleDevTools();
|
||||
}
|
||||
}
|
||||
}, {
|
||||
label: 'Toggle DevTools for Active Tab',
|
||||
accelerator: process.platform === 'darwin' ? 'Alt+Command+U' : 'Ctrl+Shift+U',
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
AppMenu.sendAction('tab-devtools');
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
getHelpSubmenu() {
|
||||
return [
|
||||
{
|
||||
label: `${appName + ' Desktop-'} v${app.getVersion()}`,
|
||||
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: 'About Zulip',
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
AppMenu.sendAction('open-about');
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: `${appName} Help`,
|
||||
label: `Help Center`,
|
||||
click() {
|
||||
shell.openExternal('https://zulipchat.com/help/');
|
||||
}
|
||||
}, {
|
||||
label: 'Show App Logs',
|
||||
click() {
|
||||
const zip = new AdmZip();
|
||||
let date = new Date();
|
||||
date = date.toLocaleDateString().replace(/\//g, '-');
|
||||
|
||||
// Create a zip file of all the logs and config data
|
||||
zip.addLocalFolder(`${app.getPath('appData')}/${appName}/Logs`);
|
||||
zip.addLocalFolder(`${app.getPath('appData')}/${appName}/config`);
|
||||
|
||||
// Put the log file in downloads folder
|
||||
const logFilePath = `${app.getPath('downloads')}/Zulip-logs-${date}.zip`;
|
||||
zip.writeZip(logFilePath);
|
||||
|
||||
// Open and select the log file
|
||||
shell.showItemInFolder(logFilePath);
|
||||
}
|
||||
}, {
|
||||
label: 'Report an Issue...',
|
||||
label: 'Report an Issue',
|
||||
click() {
|
||||
// the goal is to notify the main.html BrowserWindow
|
||||
// which may not be the focused window.
|
||||
@@ -199,32 +226,55 @@ class AppMenu {
|
||||
type: 'checkbox'
|
||||
});
|
||||
}
|
||||
initialSubmenu.push({
|
||||
type: 'separator'
|
||||
});
|
||||
initialSubmenu.push({
|
||||
label: 'Switch to Next Organization',
|
||||
accelerator: `Ctrl+Tab`,
|
||||
enabled: tabs[activeTabIndex].props.role === 'server',
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
AppMenu.sendAction('switch-server-tab', AppMenu.getNextServer(tabs, activeTabIndex));
|
||||
}
|
||||
}
|
||||
}, {
|
||||
label: 'Switch to Previous Organization',
|
||||
accelerator: `Ctrl+Shift+Tab`,
|
||||
enabled: tabs[activeTabIndex].props.role === 'server',
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
AppMenu.sendAction('switch-server-tab', AppMenu.getPreviousServer(tabs, activeTabIndex));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return initialSubmenu;
|
||||
}
|
||||
|
||||
getDarwinTpl(props) {
|
||||
const { tabs, activeTabIndex } = props;
|
||||
const { tabs, activeTabIndex, enableMenu } = props;
|
||||
|
||||
return [{
|
||||
label: `${app.getName()}`,
|
||||
submenu: [{
|
||||
label: 'About Zulip',
|
||||
label: 'Add Organization',
|
||||
accelerator: 'Cmd+Shift+N',
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
AppMenu.sendAction('open-about');
|
||||
AppMenu.sendAction('new-server');
|
||||
}
|
||||
}
|
||||
}, {
|
||||
label: `Check for Update`,
|
||||
label: 'Toggle Do Not Disturb',
|
||||
accelerator: 'Cmd+Shift+M',
|
||||
click() {
|
||||
AppMenu.checkForUpdate();
|
||||
const dndUtil = DNDUtil.toggle();
|
||||
AppMenu.sendAction('toggle-dnd', dndUtil.dnd, dndUtil.newSettings);
|
||||
}
|
||||
}, {
|
||||
type: 'separator'
|
||||
}, {
|
||||
label: 'Desktop App Settings',
|
||||
label: 'Desktop Settings',
|
||||
accelerator: 'Cmd+,',
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
@@ -234,6 +284,7 @@ class AppMenu {
|
||||
}, {
|
||||
label: 'Keyboard Shortcuts',
|
||||
accelerator: 'Cmd+Shift+K',
|
||||
enabled: enableMenu,
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
AppMenu.sendAction('shortcut');
|
||||
@@ -242,21 +293,17 @@ class AppMenu {
|
||||
}, {
|
||||
type: 'separator'
|
||||
}, {
|
||||
label: 'Toggle Do Not Disturb',
|
||||
accelerator: 'Command+Shift+M',
|
||||
click() {
|
||||
const dndUtil = DNDUtil.toggle();
|
||||
AppMenu.sendAction('toggle-dnd', dndUtil.dnd, dndUtil.newSettings);
|
||||
label: 'Copy Zulip URL',
|
||||
accelerator: 'Cmd+Shift+C',
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
AppMenu.sendAction('copy-zulip-url');
|
||||
}
|
||||
}
|
||||
}, {
|
||||
label: 'Reset App Settings',
|
||||
accelerator: 'Command+Shift+D',
|
||||
click() {
|
||||
AppMenu.resetAppSettings();
|
||||
}
|
||||
}, {
|
||||
label: 'Log Out',
|
||||
label: 'Log Out of Organization',
|
||||
accelerator: 'Cmd+L',
|
||||
enabled: enableMenu,
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
AppMenu.sendAction('log-out');
|
||||
@@ -296,8 +343,6 @@ class AppMenu {
|
||||
role: 'paste'
|
||||
}, {
|
||||
role: 'pasteandmatchstyle'
|
||||
}, {
|
||||
role: 'delete'
|
||||
}, {
|
||||
role: 'selectall'
|
||||
}]
|
||||
@@ -310,6 +355,9 @@ class AppMenu {
|
||||
}, {
|
||||
label: 'Window',
|
||||
submenu: this.getWindowSubmenu(tabs, activeTabIndex)
|
||||
}, {
|
||||
label: 'Tools',
|
||||
submenu: this.getToolsSubmenu()
|
||||
}, {
|
||||
role: 'help',
|
||||
submenu: this.getHelpSubmenu()
|
||||
@@ -317,40 +365,16 @@ class AppMenu {
|
||||
}
|
||||
|
||||
getOtherTpl(props) {
|
||||
const { tabs, activeTabIndex } = props;
|
||||
const { tabs, activeTabIndex, enableMenu } = props;
|
||||
|
||||
return [{
|
||||
label: 'File',
|
||||
label: '&File',
|
||||
submenu: [{
|
||||
label: 'About Zulip',
|
||||
label: 'Add Organization',
|
||||
accelerator: 'Ctrl+Shift+N',
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
AppMenu.sendAction('open-about');
|
||||
}
|
||||
}
|
||||
}, {
|
||||
label: `Check for Update`,
|
||||
click() {
|
||||
AppMenu.checkForUpdate();
|
||||
}
|
||||
}, {
|
||||
type: 'separator'
|
||||
}, {
|
||||
label: 'Desktop App Settings',
|
||||
accelerator: 'Ctrl+,',
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
AppMenu.sendAction('open-settings');
|
||||
}
|
||||
}
|
||||
}, {
|
||||
type: 'separator'
|
||||
}, {
|
||||
label: 'Keyboard Shortcuts',
|
||||
accelerator: 'Ctrl+Shift+K',
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
AppMenu.sendAction('shortcut');
|
||||
AppMenu.sendAction('new-server');
|
||||
}
|
||||
}
|
||||
}, {
|
||||
@@ -363,14 +387,36 @@ class AppMenu {
|
||||
AppMenu.sendAction('toggle-dnd', dndUtil.dnd, dndUtil.newSettings);
|
||||
}
|
||||
}, {
|
||||
label: 'Reset App Settings',
|
||||
accelerator: 'Ctrl+Shift+D',
|
||||
click() {
|
||||
AppMenu.resetAppSettings();
|
||||
label: 'Desktop Settings',
|
||||
accelerator: 'Ctrl+,',
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
AppMenu.sendAction('open-settings');
|
||||
}
|
||||
}
|
||||
}, {
|
||||
label: 'Log Out',
|
||||
label: 'Keyboard Shortcuts',
|
||||
accelerator: 'Ctrl+Shift+K',
|
||||
enabled: enableMenu,
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
AppMenu.sendAction('shortcut');
|
||||
}
|
||||
}
|
||||
}, {
|
||||
type: 'separator'
|
||||
}, {
|
||||
label: 'Copy Zulip URL',
|
||||
accelerator: 'Ctrl+Shift+C',
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
AppMenu.sendAction('copy-zulip-url');
|
||||
}
|
||||
}
|
||||
}, {
|
||||
label: 'Log Out of Organization',
|
||||
accelerator: 'Ctrl+L',
|
||||
enabled: enableMenu,
|
||||
click(item, focusedWindow) {
|
||||
if (focusedWindow) {
|
||||
AppMenu.sendAction('log-out');
|
||||
@@ -383,7 +429,7 @@ class AppMenu {
|
||||
accelerator: 'Ctrl+Q'
|
||||
}]
|
||||
}, {
|
||||
label: 'Edit',
|
||||
label: '&Edit',
|
||||
submenu: [{
|
||||
role: 'undo'
|
||||
}, {
|
||||
@@ -398,23 +444,25 @@ class AppMenu {
|
||||
role: 'paste'
|
||||
}, {
|
||||
role: 'pasteandmatchstyle'
|
||||
}, {
|
||||
role: 'delete'
|
||||
}, {
|
||||
type: 'separator'
|
||||
}, {
|
||||
role: 'selectall'
|
||||
}]
|
||||
}, {
|
||||
label: 'View',
|
||||
label: '&View',
|
||||
submenu: this.getViewSubmenu()
|
||||
}, {
|
||||
label: 'History',
|
||||
label: '&History',
|
||||
submenu: this.getHistorySubmenu()
|
||||
}, {
|
||||
label: 'Window',
|
||||
label: '&Window',
|
||||
submenu: this.getWindowSubmenu(tabs, activeTabIndex)
|
||||
}, {
|
||||
label: '&Tools',
|
||||
submenu: this.getToolsSubmenu()
|
||||
}, {
|
||||
label: '&Help',
|
||||
role: 'help',
|
||||
submenu: this.getHelpSubmenu()
|
||||
}];
|
||||
@@ -433,6 +481,23 @@ class AppMenu {
|
||||
static checkForUpdate() {
|
||||
appUpdater(true);
|
||||
}
|
||||
|
||||
static getNextServer(tabs, activeTabIndex) {
|
||||
do {
|
||||
activeTabIndex = (activeTabIndex + 1) % tabs.length;
|
||||
}
|
||||
while (tabs[activeTabIndex].props.role !== 'server');
|
||||
return activeTabIndex;
|
||||
}
|
||||
|
||||
static getPreviousServer(tabs, activeTabIndex) {
|
||||
do {
|
||||
activeTabIndex = (activeTabIndex - 1 + tabs.length) % tabs.length;
|
||||
}
|
||||
while (tabs[activeTabIndex].props.role !== 'server');
|
||||
return activeTabIndex;
|
||||
}
|
||||
|
||||
static resetAppSettings() {
|
||||
const resetAppSettingsMessage = 'By proceeding you will be removing all connected organizations and preferences from Zulip.';
|
||||
|
||||
|
||||
344
app/package-lock.json
generated
344
app/package-lock.json
generated
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "zulip",
|
||||
"version": "2.3.4-beta",
|
||||
"version": "2.5.0-beta",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@electron-elements/send-feedback": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@electron-elements/send-feedback/-/send-feedback-1.0.7.tgz",
|
||||
"integrity": "sha1-HmTsFMvNkmZAngeu1BDoUi45deA=",
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@electron-elements/send-feedback/-/send-feedback-1.0.8.tgz",
|
||||
"integrity": "sha512-spIZIYSe0Bp028sbZ7bcGOaUYhMyFArk+Tv7kQ4XCRNuVWRkw3SfbR8zIljZAdUavJp0wX6prSWrqLL4Dpd3Fw==",
|
||||
"requires": {
|
||||
"@electron-elements/utils": "1.0.3"
|
||||
}
|
||||
@@ -37,62 +37,105 @@
|
||||
}
|
||||
},
|
||||
"@sentry/browser": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-0.5.4.tgz",
|
||||
"integrity": "sha1-Yh/5chgrc7YoVlhLkvxl/elpBMw=",
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-4.3.4.tgz",
|
||||
"integrity": "sha512-odRXxnhcSYzyR4YvTolNEyrz3fdDVw308l+9RBRJA9yOFVlezaz1mXH6Gv00F7cIj9yE/JtezDyhP339WsWy3w==",
|
||||
"requires": {
|
||||
"@sentry/core": "0.5.4",
|
||||
"@sentry/shim": "0.5.4"
|
||||
"@sentry/core": "4.3.4",
|
||||
"@sentry/types": "4.3.4",
|
||||
"@sentry/utils": "4.3.4",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"@sentry/core": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-0.5.4.tgz",
|
||||
"integrity": "sha1-mwqEK0QhMbOAG65wviyk6cUQV04=",
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-4.3.4.tgz",
|
||||
"integrity": "sha512-KwolQmAnXiFMeXBuxPUM8fW+2bOICdHfpjdf83qD7WSeuKqGvXhxXyApWNSLE+l2DPO6/8UKnIGmR8bEn0G7QA==",
|
||||
"requires": {
|
||||
"@sentry/shim": "0.5.4"
|
||||
"@sentry/hub": "4.3.4",
|
||||
"@sentry/minimal": "4.3.4",
|
||||
"@sentry/types": "4.3.4",
|
||||
"@sentry/utils": "4.3.4",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"@sentry/electron": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/electron/-/electron-0.5.5.tgz",
|
||||
"integrity": "sha1-kInWNC22xr1sCSpTdHcIAx1ft8M=",
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/electron/-/electron-0.14.0.tgz",
|
||||
"integrity": "sha512-f0Aj2gKcX9LampOEFER0m6cC4etT1B3bsuY7E1R3vzZXcL7y5TaH2Omgk5atzAX5ycBk8LHxhVqUtE6fZBWlFA==",
|
||||
"requires": {
|
||||
"@sentry/browser": "0.5.4",
|
||||
"@sentry/core": "0.5.4",
|
||||
"@sentry/node": "0.5.4",
|
||||
"@sentry/shim": "0.5.4",
|
||||
"@sentry/utils": "0.5.4",
|
||||
"electron-fetch": "^1.1.0",
|
||||
"form-data": "^2.3.2",
|
||||
"util.promisify": "^1.0.0"
|
||||
"@sentry/browser": "~4.3.4",
|
||||
"@sentry/core": "~4.3.4",
|
||||
"@sentry/minimal": "~4.3.4",
|
||||
"@sentry/node": "~4.3.4",
|
||||
"@sentry/types": "~4.3.4",
|
||||
"@sentry/utils": "~4.3.4",
|
||||
"electron-fetch": "1.1.0",
|
||||
"form-data": "2.3.2",
|
||||
"util.promisify": "1.0.0"
|
||||
}
|
||||
},
|
||||
"@sentry/hub": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-4.3.4.tgz",
|
||||
"integrity": "sha512-vaBGCnhinLB8N4aQLMiPPhnlTkIUwU/dxWzw/xsuKY3MYWrmfMUyWgMZF60Mz3B4F0lW1lsg5jnJz9xPnjZowg==",
|
||||
"requires": {
|
||||
"@sentry/types": "4.3.4",
|
||||
"@sentry/utils": "4.3.4",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"@sentry/minimal": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-4.3.4.tgz",
|
||||
"integrity": "sha512-EBmQgdAQgxkhWFsBO4TmsP3cg5yTzg48HmPe3Dyt7PtF5Umw3DFW6qboAqnN1+KF+pHNuxkqevvgBTFp7b4Saw==",
|
||||
"requires": {
|
||||
"@sentry/hub": "4.3.4",
|
||||
"@sentry/types": "4.3.4",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"@sentry/node": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-0.5.4.tgz",
|
||||
"integrity": "sha1-s+c0nRs2EjmVkDbqrhnOvzVkw9M=",
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-4.3.4.tgz",
|
||||
"integrity": "sha512-4ro8eFnIuuYrF/TLw788xU10AYJl9HvOYC5G3jLrHXTjRNLp7jJo9lZrUexjCvwM2WF/srF3+Z0Pwr+HwdKVVw==",
|
||||
"requires": {
|
||||
"@sentry/core": "0.5.4",
|
||||
"@sentry/shim": "0.5.4",
|
||||
"raven": "^2.6.0"
|
||||
"@sentry/core": "4.3.4",
|
||||
"@sentry/hub": "4.3.4",
|
||||
"@sentry/types": "4.3.4",
|
||||
"@sentry/utils": "4.3.4",
|
||||
"@types/stack-trace": "0.0.29",
|
||||
"cookie": "0.3.1",
|
||||
"lsmod": "1.0.0",
|
||||
"md5": "2.2.1",
|
||||
"stack-trace": "0.0.10",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"@sentry/shim": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/shim/-/shim-0.5.4.tgz",
|
||||
"integrity": "sha1-y4JrGjR2WuXhsh5h3y3vL42pHcE="
|
||||
"@sentry/types": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-4.3.4.tgz",
|
||||
"integrity": "sha512-qsqrcyNilpbzYjqef+km0Grh5BckSFD4MUdJDNkUE5XU/ImniYddj18bMDlQxluJlTPDjUFQ37FXtEmxLeOwkQ=="
|
||||
},
|
||||
"@sentry/utils": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-0.5.4.tgz",
|
||||
"integrity": "sha1-jt54rOlgIW3WMv1WHIP1AGLlruE="
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-4.3.4.tgz",
|
||||
"integrity": "sha512-CMGMdIv5RHUCKRF4aWPZ/gFRTfBQpLVVJEGCeFGZLXHBdpgQac0lf3jlu8sND0KZ0S3C5x3tGS/eEqmOZRQ/pw==",
|
||||
"requires": {
|
||||
"@sentry/types": "4.3.4",
|
||||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"@sindresorhus/is": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz",
|
||||
"integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow=="
|
||||
},
|
||||
"@types/stack-trace": {
|
||||
"version": "0.0.29",
|
||||
"resolved": "https://registry.npmjs.org/@types/stack-trace/-/stack-trace-0.0.29.tgz",
|
||||
"integrity": "sha512-TgfOX+mGY/NyNxJLIbDWrO9DjGoVSW9+aB8H2yy1fy32jsvxijhmyJI9fDFgvz3YP4lvJaq9DzdR/M1bOgVc9g=="
|
||||
},
|
||||
"adm-zip": {
|
||||
"version": "0.4.11",
|
||||
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.11.tgz",
|
||||
@@ -126,7 +169,7 @@
|
||||
"argparse": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
|
||||
"integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=",
|
||||
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
|
||||
"requires": {
|
||||
"sprintf-js": "~1.0.2"
|
||||
}
|
||||
@@ -194,16 +237,16 @@
|
||||
"optional": true
|
||||
},
|
||||
"bluebird": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
|
||||
"integrity": "sha1-2VUfnemPH82h5oPRfukaBgLuLrk="
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz",
|
||||
"integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw=="
|
||||
},
|
||||
"bluebird-lst": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.5.tgz",
|
||||
"integrity": "sha1-vryDAmt+kqcocaPcWZ4hnL+wAqk=",
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.6.tgz",
|
||||
"integrity": "sha512-CBWFoPuUPpcvMUxfyr8DKdI5d4kjxFl1h39+VbKxP3KJWJHEsLtuT4pPLkjpxCGU6Ask21tvbnftWXdqIxYldQ==",
|
||||
"requires": {
|
||||
"bluebird": "^3.5.1"
|
||||
"bluebird": "^3.5.2"
|
||||
}
|
||||
},
|
||||
"boom": {
|
||||
@@ -224,28 +267,33 @@
|
||||
}
|
||||
},
|
||||
"buffer-from": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz",
|
||||
"integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ=="
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
|
||||
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A=="
|
||||
},
|
||||
"builder-util-runtime": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-4.4.0.tgz",
|
||||
"integrity": "sha512-tkTF1o7XAX79ZkMo8822ZdQMpEBGSgfJ9kEYgyTAja90BPu7HO8C02pb8iSlFXfmK0Q0UA6D8MmnSNNPi0JLeg==",
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.1.0.tgz",
|
||||
"integrity": "sha512-s1mlJ28mv+56Iebh6c9aXjVe11O3Z0cDTwAGeB0PCcUzHA37fDxGgS8ZGoYNMZP+rBHj21d/od1wuYofTVLaQg==",
|
||||
"requires": {
|
||||
"bluebird-lst": "^1.0.5",
|
||||
"debug": "^3.1.0",
|
||||
"fs-extra-p": "^4.6.1",
|
||||
"bluebird-lst": "^1.0.6",
|
||||
"debug": "^4.1.0",
|
||||
"fs-extra-p": "^7.0.0",
|
||||
"sax": "^1.2.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
||||
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -386,18 +434,12 @@
|
||||
"mimic-response": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"deep-equal": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
|
||||
"integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU="
|
||||
},
|
||||
"define-properties": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz",
|
||||
"integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
|
||||
"integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
|
||||
"requires": {
|
||||
"foreach": "^2.0.5",
|
||||
"object-keys": "^1.0.8"
|
||||
"object-keys": "^1.0.12"
|
||||
}
|
||||
},
|
||||
"delayed-stream": {
|
||||
@@ -487,46 +529,35 @@
|
||||
}
|
||||
},
|
||||
"electron-updater": {
|
||||
"version": "2.23.3",
|
||||
"resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-2.23.3.tgz",
|
||||
"integrity": "sha512-ZoQZpiEbhT3xA5Oxn7a5o4Z9adRaYs901pnTKBVBxPWmc0Qw5sZXAHkRjftlRmEn3RiEVkJtBPQSfx8kIkRcUA==",
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-4.0.6.tgz",
|
||||
"integrity": "sha512-JPGLME6fxJcHG8hX7HWFl6Aew6iVm0DkcrENreKa5SUJCHG+uUaAhxDGDt+YGcNkyx1uJ6eBGMvFxDTLUv67pg==",
|
||||
"requires": {
|
||||
"bluebird-lst": "^1.0.5",
|
||||
"builder-util-runtime": "~4.4.0",
|
||||
"electron-is-dev": "^0.3.0",
|
||||
"fs-extra-p": "^4.6.1",
|
||||
"bluebird-lst": "^1.0.6",
|
||||
"builder-util-runtime": "~8.1.0",
|
||||
"fs-extra-p": "^7.0.0",
|
||||
"js-yaml": "^3.12.0",
|
||||
"lazy-val": "^1.0.3",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"semver": "^5.5.0",
|
||||
"source-map-support": "^0.5.6"
|
||||
"pako": "^1.0.7",
|
||||
"semver": "^5.6.0",
|
||||
"source-map-support": "^0.5.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
|
||||
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA=="
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
|
||||
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"electron-window-state": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/electron-window-state/-/electron-window-state-4.1.1.tgz",
|
||||
"integrity": "sha1-azT9wxs4UU3+yLfI97XUrdtnYy0=",
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/electron-window-state/-/electron-window-state-5.0.3.tgz",
|
||||
"integrity": "sha512-1mNTwCfkolXl3kMf50yW3vE2lZj0y92P/HYWFBrb+v2S/pCka5mdwN3cagKm458A7NjndSwijynXgcLWRodsVg==",
|
||||
"requires": {
|
||||
"deep-equal": "^1.0.1",
|
||||
"jsonfile": "^2.2.3",
|
||||
"jsonfile": "^4.0.0",
|
||||
"mkdirp": "^0.5.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonfile": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
|
||||
"integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"encoding": {
|
||||
@@ -550,13 +581,13 @@
|
||||
}
|
||||
},
|
||||
"es-to-primitive": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz",
|
||||
"integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
|
||||
"integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
|
||||
"requires": {
|
||||
"is-callable": "^1.1.1",
|
||||
"is-callable": "^1.1.4",
|
||||
"is-date-object": "^1.0.1",
|
||||
"is-symbol": "^1.0.1"
|
||||
"is-symbol": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"escape-html": {
|
||||
@@ -565,9 +596,9 @@
|
||||
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
|
||||
},
|
||||
"esprima": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
|
||||
"integrity": "sha1-RJnt3NERDgshi6zy+n9/WfVcqAQ="
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
|
||||
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
|
||||
},
|
||||
"event-kit": {
|
||||
"version": "2.4.0",
|
||||
@@ -600,11 +631,6 @@
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
|
||||
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
|
||||
},
|
||||
"foreach": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz",
|
||||
"integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k="
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
|
||||
@@ -630,9 +656,9 @@
|
||||
}
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz",
|
||||
"integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==",
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
|
||||
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"jsonfile": "^4.0.0",
|
||||
@@ -640,12 +666,12 @@
|
||||
}
|
||||
},
|
||||
"fs-extra-p": {
|
||||
"version": "4.6.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra-p/-/fs-extra-p-4.6.1.tgz",
|
||||
"integrity": "sha512-IsTMbUS0svZKZTvqF4vDS9c/L7Mw9n8nZQWWeSzAGacOSe+8CzowhUN0tdZEZFIJNP5HC7L9j3MMikz/G4hDeQ==",
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra-p/-/fs-extra-p-7.0.0.tgz",
|
||||
"integrity": "sha512-5tg5jBOd0xIXjwj4PDnafOXL5TyPVzjxLby4DPKev53wurEXp7IsojBaD4Lj5M5w7jxw0pbkEU0fFEPmcKoMnA==",
|
||||
"requires": {
|
||||
"bluebird-lst": "^1.0.5",
|
||||
"fs-extra": "^6.0.1"
|
||||
"bluebird-lst": "^1.0.6",
|
||||
"fs-extra": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
@@ -733,6 +759,11 @@
|
||||
"resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz",
|
||||
"integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw=="
|
||||
},
|
||||
"has-symbols": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
|
||||
"integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q="
|
||||
},
|
||||
"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",
|
||||
@@ -778,9 +809,9 @@
|
||||
}
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.23",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
|
||||
"integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"requires": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
}
|
||||
@@ -891,9 +922,12 @@
|
||||
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
|
||||
},
|
||||
"is-symbol": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz",
|
||||
"integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI="
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
|
||||
"integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
|
||||
"requires": {
|
||||
"has-symbols": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
@@ -993,7 +1027,7 @@
|
||||
"lazy-val": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.3.tgz",
|
||||
"integrity": "sha1-u5eyAO8AgB2UwxfincbtOeMcXtw="
|
||||
"integrity": "sha512-pjCf3BYk+uv3ZcPzEVM0BFvO9Uw58TmlrU0oG5tTrr9Kcid3+kdKxapH8CjdYmVa2nO5wOoZn2rdvZx2PKj/xg=="
|
||||
},
|
||||
"lodash.assign": {
|
||||
"version": "4.2.0",
|
||||
@@ -1024,6 +1058,11 @@
|
||||
"yallist": "^2.1.2"
|
||||
}
|
||||
},
|
||||
"lsmod": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz",
|
||||
"integrity": "sha1-mgD3bco26yP6BTUK/htYXUKZ5ks="
|
||||
},
|
||||
"md5": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
|
||||
@@ -1084,30 +1123,24 @@
|
||||
"integrity": "sha1-ltDNYQ69WNS03pzAxoKM2pnHVI8="
|
||||
},
|
||||
"node-json-db": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/node-json-db/-/node-json-db-0.7.3.tgz",
|
||||
"integrity": "sha1-v2Mf9NTPQhHL3/5srmqq/m7lTN8=",
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/node-json-db/-/node-json-db-0.9.1.tgz",
|
||||
"integrity": "sha512-4BydUI7a10W8QBdHq/J3UBswU1i8WhCgTS4BZU0MjlUKrSU7cuUti71eojistgqe5hIrb4adj/wvAT5dw63NPg==",
|
||||
"requires": {
|
||||
"mkdirp": "0.5.x"
|
||||
}
|
||||
},
|
||||
"node-mac-notifier": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-mac-notifier/-/node-mac-notifier-0.1.0.tgz",
|
||||
"integrity": "sha512-K07p0jW6BhE4Jxpem4pcOdOCDgF/vy3xIqZoZpqme5JAfeW0cEkQ5Z3AmsM7fhJB59kDeOiouyykwPnvLV+hAg==",
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-mac-notifier/-/node-mac-notifier-1.1.0.tgz",
|
||||
"integrity": "sha512-Fwv09eKMGkM4xn+Eby5g7lvmYF+1KYWX4V5QNn27l4cVFVS7MNYOxhbvGGHT5VuTWbWDQIi/Lr7l6so5vvmqVw==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"bindings": "^1.2.1",
|
||||
"event-target-shim": "^1.1.1",
|
||||
"node-uuid": "^1.4.7"
|
||||
"uuid": "^3.3.2"
|
||||
}
|
||||
},
|
||||
"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",
|
||||
@@ -1196,6 +1229,11 @@
|
||||
"p-finally": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"pako": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.7.tgz",
|
||||
"integrity": "sha512-3HNK5tW4x8o5mO8RuHZp3Ydw9icZXx0RANAOMzlMzx7LVXhMJ4mo3MOBpzyd7r/+RUu8BmndP47LXT+vzjtWcQ=="
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
@@ -1309,18 +1347,6 @@
|
||||
"strict-uri-encode": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"raven": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/raven/-/raven-2.6.3.tgz",
|
||||
"integrity": "sha512-bKre7qlDW+y1+G2bUtCuntdDYc8o5v1T233t0vmJfbj8ttGOgLrGRlYB8saelVMW9KUAJNLrhFkAKOwFWFJonw==",
|
||||
"requires": {
|
||||
"cookie": "0.3.1",
|
||||
"md5": "^2.2.1",
|
||||
"stack-trace": "0.0.10",
|
||||
"timed-out": "4.0.1",
|
||||
"uuid": "3.0.0"
|
||||
}
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
|
||||
@@ -1431,7 +1457,7 @@
|
||||
"sax": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||
"integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk="
|
||||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.4.1",
|
||||
@@ -1457,12 +1483,12 @@
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM="
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.5.6",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.6.tgz",
|
||||
"integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==",
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz",
|
||||
"integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==",
|
||||
"requires": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
@@ -1539,6 +1565,11 @@
|
||||
"punycode": "^1.4.1"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
|
||||
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
@@ -1601,9 +1632,10 @@
|
||||
}
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz",
|
||||
"integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg="
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
|
||||
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
|
||||
"optional": true
|
||||
},
|
||||
"verror": {
|
||||
"version": "1.10.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "zulip",
|
||||
"productName": "Zulip",
|
||||
"version": "2.3.4-beta",
|
||||
"version": "3.0.0",
|
||||
"description": "Zulip Desktop App",
|
||||
"license": "Apache-2.0",
|
||||
"copyright": "Kandra Labs, Inc.",
|
||||
@@ -11,10 +11,10 @@
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zulip/zulip-electron.git"
|
||||
"url": "https://github.com/zulip/zulip-desktop.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/zulip/zulip-electron/issues"
|
||||
"url": "https://github.com/zulip/zulip-desktop/issues"
|
||||
},
|
||||
"main": "main/index.js",
|
||||
"keywords": [
|
||||
@@ -26,23 +26,23 @@
|
||||
"InstantMessaging"
|
||||
],
|
||||
"dependencies": {
|
||||
"@electron-elements/send-feedback": "1.0.7",
|
||||
"@sentry/electron": "0.5.5",
|
||||
"@electron-elements/send-feedback": "1.0.8",
|
||||
"@sentry/electron": "0.14.0",
|
||||
"adm-zip": "0.4.11",
|
||||
"auto-launch": "5.0.5",
|
||||
"electron-is-dev": "0.3.0",
|
||||
"electron-log": "2.2.14",
|
||||
"electron-spellchecker": "1.1.2",
|
||||
"electron-updater": "2.23.3",
|
||||
"electron-window-state": "4.1.1",
|
||||
"electron-updater": "4.0.6",
|
||||
"electron-window-state": "5.0.3",
|
||||
"escape-html": "1.0.3",
|
||||
"is-online": "7.0.0",
|
||||
"node-json-db": "0.7.3",
|
||||
"node-json-db": "0.9.1",
|
||||
"request": "2.85.0",
|
||||
"semver": "5.4.1",
|
||||
"wurl": "2.5.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"node-mac-notifier": "0.1.0"
|
||||
"node-mac-notifier": "1.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="css/about.css">
|
||||
</head>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="css/about.css">
|
||||
<title>Zulip - About</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="about">
|
||||
<img class="logo" src="../resources/zulip.png" />
|
||||
<p class="detail" id="version">v?.?.?</p>
|
||||
<div class="maintenance-info">
|
||||
<p class="detail maintainer">
|
||||
Maintained by
|
||||
<a onclick="linkInBrowser('website')">Zulip</a>
|
||||
</p>
|
||||
<p class="detail license">
|
||||
Available under the
|
||||
<a onclick="linkInBrowser('license')">Apache 2.0 License</a>
|
||||
</p>
|
||||
<a class="bug" onclick="linkInBrowser('bug')" href="#">Found bug?</a>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
<body>
|
||||
<div class="about">
|
||||
<img class="logo" src="../resources/zulip.png" />
|
||||
<p class="detail" id="version">v?.?.?</p>
|
||||
<div class="maintenance-info">
|
||||
<p class="detail maintainer">
|
||||
Maintained by
|
||||
<a onclick="linkInBrowser('website')">Zulip</a>
|
||||
</p>
|
||||
<p class="detail license">
|
||||
Available under the
|
||||
<a onclick="linkInBrowser('license')">Apache 2.0 License</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
|
||||
const { app } = require('electron').remote;
|
||||
const { shell } = require('electron');
|
||||
const version_tag = document.querySelector('#version');
|
||||
version_tag.innerHTML = 'v' + app.getVersion();
|
||||
const { app } = require('electron').remote;
|
||||
const { shell } = require('electron');
|
||||
const version_tag = document.querySelector('#version');
|
||||
version_tag.innerHTML = 'v' + app.getVersion();
|
||||
|
||||
function linkInBrowser(type) {
|
||||
let url;
|
||||
switch (type) {
|
||||
case 'website':
|
||||
url = "https://zulipchat.com";
|
||||
break;
|
||||
case 'license':
|
||||
url = "https://github.com/zulip/zulip-electron/blob/master/LICENSE";
|
||||
break;
|
||||
default:
|
||||
url = 'https://github.com/zulip/zulip-electron/issues/new?body=' +
|
||||
'%3C!--Please%20describe%20your%20issue%20and%20steps%20to%20reproduce%20it.--%3E';
|
||||
}
|
||||
shell.openExternal(url);
|
||||
}
|
||||
</script>
|
||||
<script>require('./js/shared/preventdrag.js')</script>
|
||||
</body>
|
||||
function linkInBrowser(type) {
|
||||
let url;
|
||||
switch (type) {
|
||||
case 'website':
|
||||
url = "https://zulipchat.com";
|
||||
break;
|
||||
case 'license':
|
||||
url = "https://github.com/zulip/zulip-desktop/blob/master/LICENSE";
|
||||
break;
|
||||
}
|
||||
shell.openExternal(url);
|
||||
}
|
||||
</script>
|
||||
<script>require('./js/shared/preventdrag.js')</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,82 +1,66 @@
|
||||
body {
|
||||
background: #fafafa;
|
||||
font-family: menu, "Helvetica Neue", sans-serif;
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
background: rgba(250, 250, 250, 1.000);
|
||||
font-family: menu, "Helvetica Neue", sans-serif;
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: block;
|
||||
margin: -40px auto;
|
||||
display: block;
|
||||
margin: -40px auto;
|
||||
}
|
||||
|
||||
#version {
|
||||
color: #444343;
|
||||
font-size: 1.3em;
|
||||
padding-top: 40px;
|
||||
color: rgba(68, 67, 67, 1.000);
|
||||
font-size: 1.3em;
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
.about {
|
||||
margin: 25vh auto;
|
||||
height: 25vh;
|
||||
text-align: center;
|
||||
margin: 25vh auto;
|
||||
height: 25vh;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.about p {
|
||||
font-size: 20px;
|
||||
color: rgba(0, 0, 0, 0.62);
|
||||
font-size: 20px;
|
||||
color: rgba(0, 0, 0, 0.62);
|
||||
}
|
||||
|
||||
.about img {
|
||||
width: 150px;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.detail {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.detail.maintainer {
|
||||
font-size: 1.2em;
|
||||
font-weight: 500;
|
||||
font-size: 1.2em;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.detail.license {
|
||||
font-size: 0.8em;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.maintenance-info {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
left: 0px;
|
||||
color: #444;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
left: 0px;
|
||||
color: rgba(68, 68, 68, 1.000);
|
||||
}
|
||||
|
||||
.maintenance-info p {
|
||||
margin: 0;
|
||||
font-size: 1em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.maintenance-info .bug {
|
||||
display: inline-block;
|
||||
padding: 8px 15px;
|
||||
margin-top: 30px;
|
||||
text-decoration: none;
|
||||
background-color: #52c2af;
|
||||
color: #fff;
|
||||
border-radius: 4px;
|
||||
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.maintenance-info .bug:hover {
|
||||
background-color: #32a692;
|
||||
margin: 0;
|
||||
font-size: 1em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
p.detail a {
|
||||
color: #355f4c;
|
||||
color: rgba(53, 95, 76, 1.000);
|
||||
}
|
||||
|
||||
p.detail a:hover {
|
||||
text-decoration: underline;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ body {
|
||||
}
|
||||
|
||||
.toggle-sidebar {
|
||||
background: #222c31;
|
||||
background: rgba(34, 44, 49, 1.000);
|
||||
width: 54px;
|
||||
padding: 27px 0 20px 0;
|
||||
justify-content: space-between;
|
||||
@@ -43,25 +43,25 @@ body {
|
||||
}
|
||||
|
||||
#view-controls-container {
|
||||
height: calc(100% - 208px);
|
||||
overflow-y: hidden;
|
||||
height: calc(100% - 208px);
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
#view-controls-container:hover {
|
||||
overflow-y: overlay;
|
||||
overflow-y: overlay;
|
||||
}
|
||||
|
||||
#view-controls-container::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
#view-controls-container::-webkit-scrollbar-track {
|
||||
box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
||||
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
#view-controls-container::-webkit-scrollbar-thumb {
|
||||
background-color: darkgrey;
|
||||
outline: 1px solid slategrey;
|
||||
background-color: rgba(169, 169, 169, 1.000);
|
||||
outline: 1px solid rgba(169, 169, 169, 1.000);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
@@ -119,12 +119,12 @@ body {
|
||||
}
|
||||
|
||||
.action-button i {
|
||||
color: #6c8592;
|
||||
color: rgba(108, 133, 146, 1.000);
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.action-button:hover i {
|
||||
color: #98a9b3;
|
||||
color: rgba(152, 169, 179, 1.000);
|
||||
}
|
||||
|
||||
.action-button.disable {
|
||||
@@ -136,22 +136,22 @@ body {
|
||||
}
|
||||
|
||||
.action-button.disable:hover i {
|
||||
color: #6c8592;
|
||||
color: rgba(108, 133, 146, 1.000);
|
||||
}
|
||||
|
||||
.action-button.active {
|
||||
/* background-color: rgba(255, 255, 255, 0.25); */
|
||||
background-color: #efefef;
|
||||
background-color: rgba(239, 239, 239, 1.000);
|
||||
opacity: 0.9;
|
||||
padding-right: 14px;
|
||||
}
|
||||
|
||||
.action-button.active i {
|
||||
color: #1c262b;
|
||||
color: rgba(28, 38, 43, 1.000);
|
||||
}
|
||||
|
||||
.tab:first-child {
|
||||
margin-top: 8px;
|
||||
margin-top: 9px;
|
||||
}
|
||||
|
||||
.tab {
|
||||
@@ -165,25 +165,33 @@ body {
|
||||
}
|
||||
|
||||
.tab .server-icons {
|
||||
border-radius: 50%;
|
||||
width: 30px;
|
||||
padding: 3px;
|
||||
height: 30px;
|
||||
width: 35px;
|
||||
vertical-align: top;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.tab .server-tab {
|
||||
width: 100%;
|
||||
height: 35px;
|
||||
position: relative;
|
||||
margin: 5px 0 2px 0;
|
||||
margin-top: 5px;
|
||||
z-index: 11;
|
||||
line-height: 31px;
|
||||
color: #eee;
|
||||
color: rgba(238, 238, 238, 1.000);
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
opacity: 0.6;
|
||||
padding: 2px 0;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.server-tab .alt-icon {
|
||||
font-family: Verdana;
|
||||
font-weight: 600;
|
||||
font-size: 22px;
|
||||
border: 2px solid rgba(34, 44, 49, 1.000);
|
||||
margin-left: 17%;
|
||||
width: 35px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.tab .server-tab:hover {
|
||||
@@ -208,7 +216,7 @@ body {
|
||||
|
||||
.tab.active .server-tab {
|
||||
opacity: 1;
|
||||
background-color: #648478;
|
||||
background-color: rgba(100, 132, 120, 1.000);
|
||||
}
|
||||
|
||||
.tab .server-tab-badge.active {
|
||||
@@ -216,7 +224,7 @@ body {
|
||||
min-width: 11px;
|
||||
padding: 0 3px;
|
||||
height: 17px;
|
||||
background-color: #f44336;
|
||||
background-color: rgba(244, 67, 54, 1.000);
|
||||
font-size: 10px;
|
||||
font-family: sans-serif;
|
||||
position: absolute;
|
||||
@@ -224,7 +232,7 @@ body {
|
||||
z-index: 15;
|
||||
top: 6px;
|
||||
float: right;
|
||||
color: #fff;
|
||||
color: rgba(255, 255, 255, 1.000);
|
||||
text-align: center;
|
||||
line-height: 17px;
|
||||
display: block;
|
||||
@@ -252,7 +260,7 @@ body {
|
||||
}
|
||||
|
||||
.tab .server-tab-shortcut {
|
||||
color: #648478;
|
||||
color: rgba(100, 132, 120, 1.000);
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
font-family: sans-serif;
|
||||
@@ -270,19 +278,19 @@ body {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/*Pseudo element for loading indicator*/
|
||||
/* Pseudo element for loading indicator */
|
||||
#webviews-container::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
background: #fff url(../img/ic_loading.gif) no-repeat;
|
||||
background: rgba(255, 255, 255, 1.000) url(../img/ic_loading.gif) no-repeat;
|
||||
background-size: 60px 60px;
|
||||
background-position: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/*When the active webview is loaded*/
|
||||
/* When the active webview is loaded */
|
||||
#webviews-container.loaded::before {
|
||||
opacity: 0;
|
||||
z-index: -1;
|
||||
@@ -325,27 +333,27 @@ webview.focus {
|
||||
#reload-tooltip,
|
||||
#setting-tooltip {
|
||||
font-family: sans-serif;
|
||||
background: #222c31;
|
||||
background: rgba(34, 44, 49, 1.000);
|
||||
margin-left: 48px;
|
||||
padding: 6px 8px;
|
||||
position: absolute;
|
||||
margin-top: 0px;
|
||||
z-index: 1000;
|
||||
color: white;
|
||||
color: rgba(255, 255, 255, 1.000);
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
width: 55px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#dnd-tooltip:after,
|
||||
#back-tooltip:after,
|
||||
#reload-tooltip:after,
|
||||
#setting-tooltip:after {
|
||||
#dnd-tooltip::after,
|
||||
#back-tooltip::after,
|
||||
#reload-tooltip::after,
|
||||
#setting-tooltip::after {
|
||||
content: " ";
|
||||
border-top: 8px solid transparent;
|
||||
border-bottom: 8px solid transparent;
|
||||
border-right: 8px solid #222c31;
|
||||
border-right: 8px solid rgba(34, 44, 49, 1.000);
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
right: 68px;
|
||||
@@ -354,25 +362,25 @@ webview.focus {
|
||||
#add-server-tooltip,
|
||||
.server-tooltip {
|
||||
font-family: 'arial';
|
||||
background: #222c31;
|
||||
background: rgba(34, 44, 49, 1.000);
|
||||
left: 56px;
|
||||
padding: 10px 20px;
|
||||
position: fixed;
|
||||
margin-top: 8px;
|
||||
margin-top: 11px;
|
||||
z-index: 5000 !important;
|
||||
color: #fff;
|
||||
color: rgba(255, 255, 255, 1.000);
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
width: max-content;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#add-server-tooltip:after,
|
||||
.server-tooltip:after {
|
||||
#add-server-tooltip::after,
|
||||
.server-tooltip::after {
|
||||
content: " ";
|
||||
border-top: 8px solid transparent;
|
||||
border-bottom: 8px solid transparent;
|
||||
border-right: 8px solid #222c31;
|
||||
border-right: 8px solid rgba(34, 44, 49, 1.000);
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: -5px;
|
||||
@@ -384,14 +392,14 @@ webview.focus {
|
||||
position: absolute;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: #222c31;
|
||||
background: rgba(34, 44, 49, 1.000);
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
box-shadow: #999 1px 1px;
|
||||
box-shadow: rgba(153, 153, 153, 1.000) 1px 1px;
|
||||
}
|
||||
|
||||
#collapse-button i {
|
||||
color: #efefef;
|
||||
color: rgba(239, 239, 239, 1.000);
|
||||
}
|
||||
|
||||
#main-container {
|
||||
@@ -412,8 +420,8 @@ webview.focus {
|
||||
|
||||
.popup .popuptext {
|
||||
visibility: hidden;
|
||||
background-color: #555;
|
||||
color: #fff;
|
||||
background-color: rgba(85, 85, 85, 1.000);
|
||||
color: rgba(255, 255, 255, 1.000);
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
padding: 9px 0;
|
||||
@@ -445,24 +453,24 @@ webview.focus {
|
||||
}
|
||||
|
||||
send-feedback {
|
||||
width: 60%;
|
||||
height: 85%;
|
||||
width: 60%;
|
||||
height: 85%;
|
||||
}
|
||||
|
||||
#feedback-modal {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(68, 67, 67, 0.81);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 2;
|
||||
transition: all 1s ease-out;
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(68, 67, 67, 0.81);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 2;
|
||||
transition: all 1s ease-out;
|
||||
}
|
||||
|
||||
#feedback-modal.show {
|
||||
display: flex;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
html, body {
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
cursor: default;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
user-select:none;
|
||||
color: rgba(51, 51, 51, 1.000);
|
||||
background: rgba(255, 255, 255, 1.000);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#content {
|
||||
@@ -27,8 +28,8 @@ html, body {
|
||||
|
||||
#reconnect {
|
||||
font-size: 16px;
|
||||
background: #009688;
|
||||
color: #fff;
|
||||
background: rgba(0, 150, 136, 1.000);
|
||||
color: rgba(255, 255, 255, 1.000);
|
||||
width: 84px;
|
||||
height: 32px;
|
||||
border-radius: 5px;
|
||||
|
||||
@@ -7,23 +7,25 @@ body {
|
||||
font-family: menu, "Helvetica Neue", sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
background: #efefef;
|
||||
background: rgba(239, 239, 239, 1.000);
|
||||
letter-spacing: -.08px;
|
||||
line-height: 18px;
|
||||
color: rgba(139, 142, 143, 1.000);
|
||||
}
|
||||
|
||||
kbd {
|
||||
display: inline-block;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
border: 1px solid rgba(204, 204, 204, 1.000);
|
||||
border-radius: 4px;
|
||||
font-size: 15px;
|
||||
font-family: Courier New, Courier, monospace;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
background-color: #f7f7f7;
|
||||
color: #333;
|
||||
background-color: rgba(247, 247, 247, 1.000);
|
||||
color: rgba(51, 51, 51, 1.000);
|
||||
margin: 0 0.1em;
|
||||
padding: 0.3em 0.8em;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 1.000);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
@@ -31,7 +33,7 @@ table,
|
||||
th,
|
||||
td {
|
||||
border-collapse: collapse;
|
||||
color: #383430;
|
||||
color: rgba(56, 52, 48, 1.000);
|
||||
}
|
||||
|
||||
table {
|
||||
@@ -41,11 +43,11 @@ table {
|
||||
}
|
||||
|
||||
table tr:nth-child(even) {
|
||||
background-color: #fafafa;
|
||||
background-color: rgba(250, 250, 250, 1.000);
|
||||
}
|
||||
|
||||
table tr:nth-child(odd) {
|
||||
background-color: #fff;
|
||||
background-color: rgba(255, 255, 255, 1.000);
|
||||
}
|
||||
|
||||
td {
|
||||
@@ -101,7 +103,7 @@ td:nth-child(odd) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 16px;
|
||||
background: #f2f2f2;
|
||||
background: rgba(242, 242, 242, 1.000);
|
||||
}
|
||||
|
||||
#nav-container {
|
||||
@@ -109,19 +111,19 @@ td:nth-child(odd) {
|
||||
}
|
||||
|
||||
.nav {
|
||||
padding: 5px 0;
|
||||
color: #999;
|
||||
padding: 7px 0;
|
||||
color: rgba(153, 153, 153, 1.000);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nav.active {
|
||||
color: #4ebfac;
|
||||
color: rgba(78, 191, 172, 1.000);
|
||||
cursor: default;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav.active::before {
|
||||
background: #464e5a;
|
||||
background: rgba(70, 78, 90, 1.000);
|
||||
width: 3px;
|
||||
height: 18px;
|
||||
position: absolute;
|
||||
@@ -129,7 +131,6 @@ td:nth-child(odd) {
|
||||
content: '';
|
||||
}
|
||||
|
||||
|
||||
/* We don't want to show this in nav item since we have the + button for adding an Organization */
|
||||
|
||||
#nav-AddServer {
|
||||
@@ -138,7 +139,7 @@ td:nth-child(odd) {
|
||||
|
||||
#settings-header {
|
||||
font-size: 22px;
|
||||
color: #222c31;
|
||||
color: rgba(34, 44, 49, 1.000);
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
@@ -158,11 +159,11 @@ td:nth-child(odd) {
|
||||
|
||||
.title {
|
||||
font-weight: 500;
|
||||
color: #222c31;
|
||||
color: rgba(34, 44, 49, 1.000);
|
||||
}
|
||||
|
||||
.page-title {
|
||||
color: #222c31;
|
||||
color: rgba(34, 44, 49, 1.000);
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
padding: 4px 0 6px 0;
|
||||
@@ -181,13 +182,13 @@ td:nth-child(odd) {
|
||||
.sub-title {
|
||||
padding: 4px 0 6px 0;
|
||||
font-weight: bold;
|
||||
color: #616161;
|
||||
color: rgba(97, 97, 97, 1.000);
|
||||
}
|
||||
|
||||
img.server-info-icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
padding: 4px;
|
||||
padding: 4px 4px 4px 10px;
|
||||
cursor: pointer;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@@ -246,16 +247,16 @@ img.server-info-icon {
|
||||
.setting-input-value {
|
||||
flex-grow: 1;
|
||||
font-size: 14px;
|
||||
border-radius: 3px;
|
||||
border-radius: 4px;
|
||||
padding: 13px;
|
||||
border: #ededed 2px solid;
|
||||
border: rgba(237, 237, 237, 1.000) 2px solid;
|
||||
outline-width: 0;
|
||||
background: transparent;
|
||||
max-width: 450px;
|
||||
}
|
||||
|
||||
.setting-input-value:focus {
|
||||
border: #4EBFAC 2px solid;
|
||||
border: rgba(78, 191, 172, 1.000) 2px solid;
|
||||
}
|
||||
|
||||
.manual-proxy-block {
|
||||
@@ -265,7 +266,7 @@ img.server-info-icon {
|
||||
.actions-container {
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
color: #235d3a;
|
||||
color: rgba(35, 93, 58, 1.000);
|
||||
vertical-align: middle;
|
||||
margin: 10px 0;
|
||||
flex-wrap: wrap;
|
||||
@@ -286,6 +287,7 @@ img.server-info-icon {
|
||||
|
||||
.settings-pane {
|
||||
flex-grow: 1;
|
||||
min-width: 550px;
|
||||
}
|
||||
|
||||
.action:hover {
|
||||
@@ -297,7 +299,7 @@ img.server-info-icon {
|
||||
}
|
||||
|
||||
.action.disabled {
|
||||
color: #999;
|
||||
color: rgba(153, 153, 153, 1.000);
|
||||
}
|
||||
|
||||
.settings-card {
|
||||
@@ -305,8 +307,8 @@ img.server-info-icon {
|
||||
flex-wrap: wrap;
|
||||
padding: 12px 30px;
|
||||
margin: 10px 0 20px 0;
|
||||
background: #fff;
|
||||
width: 70%;
|
||||
background: rgba(255, 255, 255, 1.000);
|
||||
width: 80%;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
@@ -320,29 +322,59 @@ img.server-info-icon {
|
||||
}
|
||||
|
||||
.red {
|
||||
color: #ef5350;
|
||||
padding: 8px;
|
||||
border: rgba(239, 83, 80, 0.5) solid 1px;
|
||||
color: rgb(240, 148, 148);
|
||||
background: rgba(255, 255, 255, 1.000);
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
border: 2px solid rgb(240, 148, 148);
|
||||
padding: 10px;
|
||||
width: 100px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
transition: background-color 0.2s ease;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.red:hover {
|
||||
color: #e63431;
|
||||
border: rgba(239, 83, 80, 0.7) solid 1px;
|
||||
;
|
||||
background-color: rgb(240, 148, 148);
|
||||
color: rgba(255, 255, 255, 1.000);
|
||||
}
|
||||
|
||||
.blue {
|
||||
color: #ffffff;
|
||||
background: #4EBFAC;
|
||||
padding: 3px;
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
.green {
|
||||
color: rgba(255, 255, 255, 1.000);
|
||||
background: rgba(78, 191, 172, 1.000);
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
width: 100px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
font-weight: bold;
|
||||
transition: background-color 0.2s ease;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.green:hover {
|
||||
background-color: rgba(60, 159, 141, 1.000);
|
||||
color: rgba(255, 255, 255, 1.000);
|
||||
}
|
||||
|
||||
.w-150 {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.w-250 {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.grey {
|
||||
color: #9E9E9E;
|
||||
background: #FAFAFA;
|
||||
border: 1px solid #9E9E9E;
|
||||
color: rgba(158, 158, 158, 1.000);
|
||||
background: rgba(250, 250, 250, 1.000);
|
||||
border: 1px solid rgba(158, 158, 158, 1.000);
|
||||
}
|
||||
|
||||
.setting-row {
|
||||
@@ -358,38 +390,19 @@ img.server-info-icon {
|
||||
}
|
||||
|
||||
i.open-tab-button {
|
||||
padding-left: 2px;
|
||||
padding-left: 5px;
|
||||
font-size: 19px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.reset-data-button,
|
||||
.custom-css-button,
|
||||
.download-folder-button {
|
||||
display: inline-block;
|
||||
border: none;
|
||||
padding: 10px;
|
||||
width: 120px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
transition: background-color 0.2s ease;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.css-delete-action {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.reset-data-button:hover {
|
||||
background-color: #3c9f8d;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.selected-css-path,
|
||||
.download-folder-path {
|
||||
background: #eeeeee;
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
background: rgba(238, 238, 238, 1.000);
|
||||
padding: 5px 10px;
|
||||
margin-right: 10px;
|
||||
display: flex;
|
||||
width: 90%;
|
||||
@@ -401,7 +414,11 @@ i.open-tab-button {
|
||||
}
|
||||
|
||||
#server-info-container {
|
||||
min-height: calc(100% - 260px);
|
||||
/* min-height: calc(100% - 260px); */
|
||||
}
|
||||
|
||||
#new-org-button {
|
||||
margin: 30px 0px 30px 0px;
|
||||
}
|
||||
|
||||
#create-organization-container {
|
||||
@@ -424,7 +441,7 @@ i.open-tab-button {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.toggle+label {
|
||||
.toggle + label {
|
||||
display: block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
@@ -432,16 +449,16 @@ i.open-tab-button {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
input.toggle-round+label {
|
||||
input.toggle-round + label {
|
||||
padding: 2px;
|
||||
width: 50px;
|
||||
height: 25px;
|
||||
background-color: #dddddd;
|
||||
background-color: rgba(221, 221, 221, 1.000);
|
||||
border-radius: 25px;
|
||||
}
|
||||
|
||||
input.toggle-round+label:before,
|
||||
input.toggle-round+label:after {
|
||||
input.toggle-round + label::before,
|
||||
input.toggle-round + label::after {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
@@ -450,28 +467,34 @@ input.toggle-round+label:after {
|
||||
content: "";
|
||||
}
|
||||
|
||||
input.toggle-round+label:before {
|
||||
right: 2px;
|
||||
background-color: #f1f1f1;
|
||||
input.toggle-round + label::before {
|
||||
background-color: rgba(241, 241, 241, 1.000);
|
||||
border-radius: 25px;
|
||||
top: 0;
|
||||
right: 0px;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
input.toggle-round+label:after {
|
||||
input.toggle-round + label::after {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
background-color: #fff;
|
||||
background-color: rgba(255, 255, 255, 1.000);
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
input.toggle-round:checked+label:before {
|
||||
background-color: #4EBFAC;
|
||||
input.toggle-round:checked + label::before {
|
||||
background-color: rgba(78, 191, 172, 1.000);
|
||||
top: 0;
|
||||
right: 0px;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
input.toggle-round:checked+label:after {
|
||||
input.toggle-round:checked + label::after {
|
||||
margin-left: 25px;
|
||||
}
|
||||
|
||||
|
||||
/* Add new server modal */
|
||||
|
||||
.add-server-modal {
|
||||
@@ -485,18 +508,17 @@ input.toggle-round:checked+label:after {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/* background: rgba(61, 64, 67, 15); */
|
||||
background: linear-gradient(35deg, #003b52, #45b59b);
|
||||
background: linear-gradient(35deg, rgba(0, 59, 82, 1.000), rgba(69, 181, 155, 1.000));
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
|
||||
/* Modal Content */
|
||||
|
||||
.modal-container {
|
||||
background-color: #f4f7f8;
|
||||
background-color: rgba(244, 247, 248, 1.000);
|
||||
margin: auto;
|
||||
padding: 57px;
|
||||
border: #dae1e3 1px solid;
|
||||
border: rgba(218, 225, 227, 1.000) 1px solid;
|
||||
width: 550px;
|
||||
height: 370px;
|
||||
border-radius: 4px;
|
||||
@@ -510,7 +532,7 @@ input.toggle-round:checked+label:after {
|
||||
.divider {
|
||||
margin-bottom: 30px;
|
||||
margin-top: 30px;
|
||||
color: #7d878a;
|
||||
color: rgba(125, 135, 138, 1.000);
|
||||
}
|
||||
|
||||
.divider hr {
|
||||
@@ -541,53 +563,54 @@ input.toggle-round:checked+label:after {
|
||||
margin: auto;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
background: #4EBFAC;
|
||||
color: rgba(255, 255, 255, 1.000);
|
||||
background: rgba(78, 191, 172, 1.000);
|
||||
border-color: none;
|
||||
border: none;
|
||||
width: 98%;
|
||||
height: 46px;
|
||||
border-radius: 3px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.server-center button:hover {
|
||||
background: #329588;
|
||||
background: rgba(50, 149, 136, 1.000);
|
||||
}
|
||||
|
||||
.server-center button:focus {
|
||||
background: #329588;
|
||||
background: rgba(50, 149, 136, 1.000);
|
||||
}
|
||||
|
||||
.certificates-card {
|
||||
width:70%
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.certificate-input {
|
||||
width:100%;
|
||||
margin-top: 10px;
|
||||
display:inline-flex;
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.certificate-input div {
|
||||
align-self:center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.certificate-input .setting-input-value {
|
||||
margin-left:10px;
|
||||
margin-left: 10px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
#add-certificate-button {
|
||||
width:20%;
|
||||
margin-right:0px;
|
||||
height: 35px;
|
||||
margin: 10px 10px 10px 37px;
|
||||
}
|
||||
|
||||
.tip {
|
||||
background-color: hsl(46,63%,95%);
|
||||
border: 1px solid hsl(46,63%,84%);
|
||||
border-radius: 4px;
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.tip:hover {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.md-14 {
|
||||
@@ -605,7 +628,7 @@ input.toggle-round:checked+label:after {
|
||||
|
||||
@media (max-width: 650px) {
|
||||
.selected-css-path,
|
||||
.download-folder-path {
|
||||
.download-folder-path {
|
||||
margin-right: 15px;
|
||||
}
|
||||
#css-delete-action {
|
||||
@@ -655,12 +678,10 @@ input.toggle-round:checked+label:after {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.server-info-right {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.action {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* Override css rules */
|
||||
|
||||
.portico-wrap>.header {
|
||||
.portico-wrap > .header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.portico-container>.footer {
|
||||
.portico-container > .footer {
|
||||
display: none;
|
||||
}
|
||||
@@ -10,24 +10,36 @@ function handleExternalLink(event) {
|
||||
const { url } = event;
|
||||
const domainPrefix = DomainUtil.getDomain(this.props.index).url;
|
||||
const downloadPath = ConfigUtil.getConfigItem('downloadsPath', `${app.getPath('downloads')}`);
|
||||
// Whitelist URLs which are allowed to be opened in the app
|
||||
const shouldShowInFolder = ConfigUtil.getConfigItem('showDownloadFolder', false);
|
||||
|
||||
// Whitelist URLs which are allowed to be opened in the app
|
||||
const {
|
||||
isInternalUrl: isWhiteListURL,
|
||||
isUploadsUrl: isUploadsURL
|
||||
} = LinkUtil.isInternal(domainPrefix, url);
|
||||
isInternalUrl: isWhiteListURL,
|
||||
isUploadsUrl: isUploadsURL
|
||||
} = LinkUtil.isInternal(domainPrefix, url);
|
||||
|
||||
if (isWhiteListURL) {
|
||||
event.preventDefault();
|
||||
|
||||
// download txt, pdf, mp3, mp4 etc.. by using downloadURL in the
|
||||
// main process which allows the user to save the files to their desktop
|
||||
// and not trigger webview reload while image in webview will
|
||||
// do nothing and will not save it
|
||||
// Code to show pdf in a new BrowserWindow (currently commented out due to bug-upstream)
|
||||
// Show pdf attachments in a new window
|
||||
// if (LinkUtil.isPDF(url) && isUploadsURL) {
|
||||
// ipcRenderer.send('pdf-view', url);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// download txt, mp3, mp4 etc.. by using downloadURL in the
|
||||
// main process which allows the user to save the files to their desktop
|
||||
// and not trigger webview reload while image in webview will
|
||||
// do nothing and will not save it
|
||||
|
||||
// Code to show pdf in a new BrowserWindow (currently commented out due to bug-upstream)
|
||||
// if (!LinkUtil.isImage(url) && !LinkUtil.isPDF(url) && isUploadsURL) {
|
||||
if (!LinkUtil.isImage(url) && isUploadsURL) {
|
||||
ipcRenderer.send('downloadFile', url, downloadPath);
|
||||
ipcRenderer.once('downloadFileCompleted', (event, filePath, fileName) => {
|
||||
const downloadNotification = new Notification('Download Complete', {
|
||||
body: `Click to open ${fileName}`,
|
||||
body: shouldShowInFolder ? `Click to show ${fileName} in folder` : `Click to open ${fileName}`,
|
||||
silent: true // We'll play our own sound - ding.ogg
|
||||
});
|
||||
|
||||
@@ -37,7 +49,13 @@ function handleExternalLink(event) {
|
||||
}
|
||||
|
||||
downloadNotification.onclick = () => {
|
||||
shell.openItem(filePath);
|
||||
if (shouldShowInFolder) {
|
||||
// Reveal file in download folder
|
||||
shell.showItemInFolder(filePath);
|
||||
} else {
|
||||
// Open file in the default native app
|
||||
shell.openItem(filePath);
|
||||
}
|
||||
};
|
||||
ipcRenderer.removeAllListeners('downloadFileFailed');
|
||||
});
|
||||
@@ -51,7 +69,7 @@ function handleExternalLink(event) {
|
||||
return;
|
||||
}
|
||||
|
||||
// open internal urls inside the current webview.
|
||||
// open internal urls inside the current webview.
|
||||
this.$el.loadURL(url);
|
||||
} else {
|
||||
event.preventDefault();
|
||||
|
||||
@@ -8,7 +8,7 @@ const {ipcRenderer} = require('electron');
|
||||
class ServerTab extends Tab {
|
||||
template() {
|
||||
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">${this.props.name}</div>
|
||||
<div class="server-tab-badge"></div>
|
||||
<div class="server-tab">
|
||||
<img class="server-icons" src='${this.props.icon}'/>
|
||||
|
||||
@@ -33,6 +33,7 @@ class WebView extends BaseComponent {
|
||||
disablewebsecurity
|
||||
${this.props.preload ? 'preload="js/preload.js"' : ''}
|
||||
partition="persist:webviewsession"
|
||||
name="${this.props.name}"
|
||||
webpreferences="allowRunningInsecureContent, javascript=yes">
|
||||
</webview>`;
|
||||
}
|
||||
@@ -94,6 +95,11 @@ class WebView extends BaseComponent {
|
||||
}
|
||||
this.loading = false;
|
||||
this.show();
|
||||
|
||||
// Refocus text boxes after reload
|
||||
// Remove when upstream issue https://github.com/electron/electron/issues/14474 is fixed
|
||||
this.$el.blur();
|
||||
this.$el.focus();
|
||||
});
|
||||
|
||||
this.$el.addEventListener('did-fail-load', event => {
|
||||
@@ -164,7 +170,13 @@ class WebView extends BaseComponent {
|
||||
focus() {
|
||||
// focus Webview and it's contents when Window regain focus.
|
||||
const webContents = this.$el.getWebContents();
|
||||
if (webContents && !webContents.isFocused()) {
|
||||
// HACK: webContents.isFocused() seems to be true even without the element
|
||||
// being in focus. So, we check against `document.activeElement`.
|
||||
if (webContents && this.$el !== document.activeElement) {
|
||||
// HACK: Looks like blur needs to be called on the previously focused
|
||||
// element to transfer focus correctly, in Electron v3.0.10
|
||||
// See https://github.com/electron/electron/issues/15718
|
||||
document.activeElement.blur();
|
||||
this.$el.focus();
|
||||
webContents.focus();
|
||||
}
|
||||
@@ -213,6 +225,7 @@ class WebView extends BaseComponent {
|
||||
back() {
|
||||
if (this.$el.canGoBack()) {
|
||||
this.$el.goBack();
|
||||
this.focus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
40
app/renderer/js/electron-bridge.js
Normal file
40
app/renderer/js/electron-bridge.js
Normal file
@@ -0,0 +1,40 @@
|
||||
const events = require('events');
|
||||
const { ipcRenderer } = require('electron');
|
||||
|
||||
// we have and will have some non camelcase stuff
|
||||
// while working with zulip so just turning the rule off
|
||||
// for the whole file.
|
||||
/* eslint-disable camelcase */
|
||||
class ElectronBridge extends events {
|
||||
send_event(...args) {
|
||||
this.emit(...args);
|
||||
}
|
||||
|
||||
on_event(...args) {
|
||||
this.on(...args);
|
||||
}
|
||||
}
|
||||
|
||||
const electron_bridge = new ElectronBridge();
|
||||
|
||||
electron_bridge.on('total_unread_count', (...args) => {
|
||||
ipcRenderer.send('unread-count', ...args);
|
||||
});
|
||||
|
||||
electron_bridge.on('realm_name', realmName => {
|
||||
const serverURL = location.origin;
|
||||
ipcRenderer.send('realm-name-changed', serverURL, realmName);
|
||||
});
|
||||
|
||||
electron_bridge.on('realm_icon_url', iconURL => {
|
||||
const serverURL = location.origin;
|
||||
iconURL = iconURL.includes('http') ? iconURL : `${serverURL}${iconURL}`;
|
||||
ipcRenderer.send('realm-icon-changed', serverURL, iconURL);
|
||||
});
|
||||
|
||||
// this follows node's idiomatic implementation of event
|
||||
// emitters to make event handling more simpler instead of using
|
||||
// functions zulip side will emit event using ElectronBrigde.send_event
|
||||
// which is alias of .emit and on this side we can handle the data by adding
|
||||
// a listener for the event.
|
||||
module.exports = electron_bridge;
|
||||
@@ -1,9 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
const { ipcRenderer, remote } = require('electron');
|
||||
const { ipcRenderer, remote, clipboard } = require('electron');
|
||||
const isDev = require('electron-is-dev');
|
||||
|
||||
const { session, app } = remote;
|
||||
const { session, app, Menu, dialog } = remote;
|
||||
const escape = require('escape-html');
|
||||
|
||||
require(__dirname + '/js/tray.js');
|
||||
const DomainUtil = require(__dirname + '/js/utils/domain-util.js');
|
||||
@@ -14,9 +15,9 @@ const ConfigUtil = require(__dirname + '/js/utils/config-util.js');
|
||||
const DNDUtil = require(__dirname + '/js/utils/dnd-util.js');
|
||||
const ReconnectUtil = require(__dirname + '/js/utils/reconnect-util.js');
|
||||
const Logger = require(__dirname + '/js/utils/logger-util.js');
|
||||
const { feedbackHolder } = require(__dirname + '/js/feedback.js');
|
||||
const CommonUtil = require(__dirname + '/js/utils/common-util.js');
|
||||
|
||||
const escape = require('escape-html');
|
||||
const { feedbackHolder } = require(__dirname + '/js/feedback.js');
|
||||
|
||||
const logger = new Logger({
|
||||
file: 'errors.log',
|
||||
@@ -104,12 +105,14 @@ class ServerManagerView {
|
||||
useSystemProxy: false,
|
||||
showSidebar: true,
|
||||
badgeOption: true,
|
||||
startAtLogin: false,
|
||||
startAtLogin: true,
|
||||
startMinimized: false,
|
||||
enableSpellchecker: true,
|
||||
showNotification: true,
|
||||
autoUpdate: true,
|
||||
betaUpdate: false,
|
||||
errorReporting: true,
|
||||
customCSS: false,
|
||||
silent: false,
|
||||
lastActiveTab: 0,
|
||||
dnd: false,
|
||||
@@ -117,7 +120,8 @@ class ServerManagerView {
|
||||
showNotification: true,
|
||||
silent: false
|
||||
},
|
||||
downloadsPath: `${app.getPath('downloads')}`
|
||||
downloadsPath: `${app.getPath('downloads')}`,
|
||||
showDownloadFolder: false
|
||||
};
|
||||
|
||||
// Platform specific settings
|
||||
@@ -133,6 +137,10 @@ class ServerManagerView {
|
||||
settingOptions.dockBouncing = true;
|
||||
}
|
||||
|
||||
if (process.platform !== 'darwin') {
|
||||
settingOptions.autoHideMenubar = false;
|
||||
}
|
||||
|
||||
for (const i in settingOptions) {
|
||||
if (ConfigUtil.getConfigItem(i) === null) {
|
||||
ConfigUtil.setConfigItem(i, settingOptions[i]);
|
||||
@@ -167,18 +175,20 @@ class ServerManagerView {
|
||||
this.tabs.push(new ServerTab({
|
||||
role: 'server',
|
||||
icon: server.icon,
|
||||
name: server.alias,
|
||||
$root: this.$tabsContainer,
|
||||
onClick: this.activateLastTab.bind(this, index),
|
||||
index,
|
||||
tabIndex,
|
||||
onHover: this.onHover.bind(this, index, server.alias),
|
||||
onHover: this.onHover.bind(this, index),
|
||||
onHoverOut: this.onHoverOut.bind(this, index),
|
||||
webview: new WebView({
|
||||
$root: this.$webviewsContainer,
|
||||
index,
|
||||
tabIndex,
|
||||
url: server.url,
|
||||
name: server.alias,
|
||||
role: 'server',
|
||||
name: CommonUtil.decodeString(server.alias),
|
||||
isActive: () => {
|
||||
return index === this.activeTabIndex;
|
||||
},
|
||||
@@ -192,6 +202,24 @@ class ServerManagerView {
|
||||
|
||||
initActions() {
|
||||
this.initDNDButton();
|
||||
this.initServerActions();
|
||||
this.initLeftSidebarEvents();
|
||||
}
|
||||
|
||||
initServerActions() {
|
||||
const $serverImgs = document.querySelectorAll('.server-icons');
|
||||
$serverImgs.forEach(($serverImg, index) => {
|
||||
this.addContextMenu($serverImg, index);
|
||||
if ($serverImg.src.includes('img/icon.png')) {
|
||||
this.displayInitialCharLogo($serverImg, index);
|
||||
}
|
||||
$serverImg.addEventListener('error', () => {
|
||||
this.displayInitialCharLogo($serverImg, index);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
initLeftSidebarEvents() {
|
||||
this.$dndButton.addEventListener('click', () => {
|
||||
const dndUtil = DNDUtil.toggle();
|
||||
ipcRenderer.send('forward-message', 'toggle-dnd', dndUtil.dnd, dndUtil.newSettings);
|
||||
@@ -209,13 +237,6 @@ class ServerManagerView {
|
||||
this.tabs[this.activeTabIndex].webview.back();
|
||||
});
|
||||
|
||||
const $serverImgs = document.querySelectorAll('.server-icons');
|
||||
$serverImgs.forEach($serverImg => {
|
||||
$serverImg.addEventListener('error', () => {
|
||||
$serverImg.src = 'img/icon.png';
|
||||
});
|
||||
});
|
||||
|
||||
this.sidebarHoverEvent(this.$addServerButton, this.$addServerTooltip, true);
|
||||
this.sidebarHoverEvent(this.$settingsButton, this.$settingsTooltip);
|
||||
this.sidebarHoverEvent(this.$reloadButton, this.$reloadTooltip);
|
||||
@@ -234,6 +255,34 @@ class ServerManagerView {
|
||||
return currentIndex;
|
||||
}
|
||||
|
||||
displayInitialCharLogo($img, index) {
|
||||
/*
|
||||
index parameter needed because webview[data-tab-id] can increment
|
||||
beyond size of sidebar org array and throw error
|
||||
*/
|
||||
|
||||
const $altIcon = document.createElement('div');
|
||||
const $parent = $img.parentElement;
|
||||
const $container = $parent.parentElement;
|
||||
const webviewId = $container.dataset.tabId;
|
||||
const $webview = document.querySelector(`webview[data-tab-id="${webviewId}"]`);
|
||||
const realmName = $webview.getAttribute('name');
|
||||
|
||||
if (realmName === null) {
|
||||
$img.src = '/img/icon.png';
|
||||
return;
|
||||
}
|
||||
|
||||
$altIcon.textContent = realmName.charAt(0) || 'Z';
|
||||
$altIcon.classList.add('server-icon');
|
||||
$altIcon.classList.add('alt-icon');
|
||||
|
||||
$parent.removeChild($img);
|
||||
$parent.appendChild($altIcon);
|
||||
|
||||
this.addContextMenu($altIcon, index);
|
||||
}
|
||||
|
||||
sidebarHoverEvent(SidebarButton, SidebarTooltip, addServer = false) {
|
||||
SidebarButton.addEventListener('mouseover', () => {
|
||||
SidebarTooltip.removeAttribute('style');
|
||||
@@ -251,8 +300,9 @@ class ServerManagerView {
|
||||
});
|
||||
}
|
||||
|
||||
onHover(index, serverName) {
|
||||
this.$serverIconTooltip[index].innerHTML = escape(serverName);
|
||||
onHover(index) {
|
||||
// this.$serverIconTooltip[index].innerHTML already has realm name, so we are just
|
||||
// removing the style.
|
||||
this.$serverIconTooltip[index].removeAttribute('style');
|
||||
// To handle position of servers' tooltip due to scrolling of list of organizations
|
||||
// This could not be handled using CSS, hence the top of the tooltip is made same
|
||||
@@ -274,6 +324,7 @@ class ServerManagerView {
|
||||
this.functionalTabs[tabProps.name] = this.tabs.length;
|
||||
|
||||
const tabIndex = this.getTabIndex();
|
||||
|
||||
this.tabs.push(new FunctionalTab({
|
||||
role: 'function',
|
||||
materialIcon: tabProps.materialIcon,
|
||||
@@ -288,6 +339,7 @@ class ServerManagerView {
|
||||
index: this.functionalTabs[tabProps.name],
|
||||
tabIndex,
|
||||
url: tabProps.url,
|
||||
role: 'function',
|
||||
name: tabProps.name,
|
||||
isActive: () => {
|
||||
return this.functionalTabs[tabProps.name] === this.activeTabIndex;
|
||||
@@ -298,9 +350,11 @@ class ServerManagerView {
|
||||
preload: false
|
||||
})
|
||||
}));
|
||||
|
||||
// To show loading indicator the first time a functional tab is opened, indicator is
|
||||
// closed when the functional tab DOM is ready, handled in webview.js
|
||||
this.$webviewsContainer.classList.remove('loaded');
|
||||
|
||||
this.activateTab(this.functionalTabs[tabProps.name]);
|
||||
}
|
||||
|
||||
@@ -337,6 +391,25 @@ class ServerManagerView {
|
||||
ConfigUtil.setConfigItem('lastActiveTab', index);
|
||||
}
|
||||
|
||||
// returns this.tabs in an way that does
|
||||
// not crash app when this.tabs is passed into
|
||||
// ipcRenderer. Something about webview, and props.webview
|
||||
// properties in ServerTab causes the app to crash.
|
||||
get tabsForIpc() {
|
||||
const tabs = [];
|
||||
this.tabs.forEach(tab => {
|
||||
const proto = Object.create(Object.getPrototypeOf(tab));
|
||||
const tabClone = Object.assign(proto, tab);
|
||||
|
||||
tabClone.webview = { props: {} };
|
||||
tabClone.webview.props.name = tab.webview.props.name;
|
||||
delete tabClone.props.webview;
|
||||
tabs.push(tabClone);
|
||||
});
|
||||
|
||||
return tabs;
|
||||
}
|
||||
|
||||
activateTab(index, hideOldTab = true) {
|
||||
if (!this.tabs[index]) {
|
||||
return;
|
||||
@@ -363,38 +436,12 @@ class ServerManagerView {
|
||||
this.tabs[index].activate();
|
||||
|
||||
ipcRenderer.send('update-menu', {
|
||||
tabs: this.tabs,
|
||||
activeTabIndex: this.activeTabIndex
|
||||
});
|
||||
|
||||
ipcRenderer.on('toggle-sidebar', (event, state) => {
|
||||
const selector = 'webview:not([class*=disabled])';
|
||||
const webview = document.querySelector(selector);
|
||||
const webContents = webview.getWebContents();
|
||||
webContents.send('toggle-sidebar', state);
|
||||
});
|
||||
|
||||
ipcRenderer.on('toggle-silent', (event, state) => {
|
||||
const webviews = document.querySelectorAll('webview');
|
||||
webviews.forEach(webview => {
|
||||
try {
|
||||
webview.setAudioMuted(state);
|
||||
} catch (err) {
|
||||
// webview is not ready yet
|
||||
webview.addEventListener('dom-ready', () => {
|
||||
webview.isAudioMuted();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
ipcRenderer.on('toggle-dnd', (event, state, newSettings) => {
|
||||
this.toggleDNDButton(state);
|
||||
ipcRenderer.send('forward-message', 'toggle-silent', newSettings.silent);
|
||||
const selector = 'webview:not([class*=disabled])';
|
||||
const webview = document.querySelector(selector);
|
||||
const webContents = webview.getWebContents();
|
||||
webContents.send('toggle-dnd', state, newSettings);
|
||||
// JSON stringify this.tabs to avoid a crash
|
||||
// util.inspect is being used to handle circular references
|
||||
tabs: this.tabsForIpc,
|
||||
activeTabIndex: this.activeTabIndex,
|
||||
// Following flag controls whether a menu item should be enabled or not
|
||||
enableMenu: this.tabs[index].props.role === 'server'
|
||||
});
|
||||
}
|
||||
|
||||
@@ -436,6 +483,7 @@ class ServerManagerView {
|
||||
// Destroy the current view and re-initiate it
|
||||
this.destroyView();
|
||||
this.initTabs();
|
||||
this.initServerActions();
|
||||
}
|
||||
|
||||
// This will trigger when pressed CTRL/CMD + R [WIP]
|
||||
@@ -467,10 +515,42 @@ class ServerManagerView {
|
||||
|
||||
// Toggles the dnd button icon.
|
||||
toggleDNDButton(alert) {
|
||||
this.$dndTooltip.textContent = (alert ? 'Turn Off' : 'Enable') + ' Do Not Disturb';
|
||||
this.$dndTooltip.textContent = (alert ? 'Disable' : 'Enable') + ' Do Not Disturb';
|
||||
this.$dndButton.querySelector('i').textContent = alert ? 'notifications_off' : 'notifications';
|
||||
}
|
||||
|
||||
addContextMenu($serverImg, index) {
|
||||
$serverImg.addEventListener('contextmenu', e => {
|
||||
e.preventDefault();
|
||||
const template = [
|
||||
{
|
||||
label: 'Disconnect organization',
|
||||
click: () => {
|
||||
dialog.showMessageBox({
|
||||
type: 'warning',
|
||||
buttons: ['YES', 'NO'],
|
||||
defaultId: 0,
|
||||
message: 'Are you sure you want to disconnect this organization?'
|
||||
}, response => {
|
||||
if (response === 0) {
|
||||
DomainUtil.removeDomain(index);
|
||||
ipcRenderer.send('reload-full-app');
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Copy Zulip URL',
|
||||
click: () => {
|
||||
clipboard.writeText(DomainUtil.getDomain(index).url);
|
||||
}
|
||||
}
|
||||
];
|
||||
const contextMenu = Menu.buildFromTemplate(template);
|
||||
contextMenu.popup({ window: remote.getCurrentWindow() });
|
||||
});
|
||||
}
|
||||
|
||||
registerIpcs() {
|
||||
const webviewListeners = {
|
||||
'webview-reload': 'reload',
|
||||
@@ -516,6 +596,10 @@ class ServerManagerView {
|
||||
this.activateLastTab(index);
|
||||
});
|
||||
|
||||
ipcRenderer.on('open-org-tab', () => {
|
||||
this.openSettings('AddServer');
|
||||
});
|
||||
|
||||
ipcRenderer.on('reload-proxy', (event, showAlert) => {
|
||||
this.loadProxy().then(() => {
|
||||
if (showAlert) {
|
||||
@@ -526,7 +610,74 @@ class ServerManagerView {
|
||||
});
|
||||
|
||||
ipcRenderer.on('toggle-sidebar', (event, show) => {
|
||||
// Toggle the left sidebar
|
||||
this.toggleSidebar(show);
|
||||
|
||||
// Toggle sidebar switch in the general settings
|
||||
const selector = 'webview:not([class*=disabled])';
|
||||
const webview = document.querySelector(selector);
|
||||
const webContents = webview.getWebContents();
|
||||
webContents.send('toggle-sidebar-setting', show);
|
||||
});
|
||||
|
||||
ipcRenderer.on('toggle-silent', (event, state) => {
|
||||
const webviews = document.querySelectorAll('webview');
|
||||
webviews.forEach(webview => {
|
||||
try {
|
||||
webview.setAudioMuted(state);
|
||||
} catch (err) {
|
||||
// webview is not ready yet
|
||||
webview.addEventListener('dom-ready', () => {
|
||||
webview.setAudioMuted(state);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
ipcRenderer.on('toggle-dnd', (event, state, newSettings) => {
|
||||
this.toggleDNDButton(state);
|
||||
ipcRenderer.send('forward-message', 'toggle-silent', newSettings.silent);
|
||||
const selector = 'webview:not([class*=disabled])';
|
||||
const webview = document.querySelector(selector);
|
||||
const webContents = webview.getWebContents();
|
||||
webContents.send('toggle-dnd', state, newSettings);
|
||||
});
|
||||
|
||||
ipcRenderer.on('update-realm-name', (event, serverURL, realmName) => {
|
||||
DomainUtil.getDomains().forEach((domain, index) => {
|
||||
if (domain.url.includes(serverURL)) {
|
||||
const serverTooltipSelector = `.tab .server-tooltip`;
|
||||
const serverTooltips = document.querySelectorAll(serverTooltipSelector);
|
||||
serverTooltips[index].innerHTML = escape(realmName);
|
||||
this.tabs[index].props.name = escape(realmName);
|
||||
this.tabs[index].webview.props.name = realmName;
|
||||
|
||||
domain.alias = escape(realmName);
|
||||
DomainUtil.db.push(`/domains[${index}]`, domain, true);
|
||||
DomainUtil.reloadDB();
|
||||
// Update the realm name also on the Window menu
|
||||
ipcRenderer.send('update-menu', {
|
||||
tabs: this.tabsForIpc,
|
||||
activeTabIndex: this.activeTabIndex
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
ipcRenderer.on('update-realm-icon', (event, serverURL, iconURL) => {
|
||||
DomainUtil.getDomains().forEach((domain, index) => {
|
||||
if (domain.url.includes(serverURL)) {
|
||||
DomainUtil.saveServerIcon(iconURL).then(localIconUrl => {
|
||||
const serverImgsSelector = `.tab .server-icons`;
|
||||
const serverImgs = document.querySelectorAll(serverImgsSelector);
|
||||
serverImgs[index].src = localIconUrl;
|
||||
|
||||
domain.icon = localIconUrl;
|
||||
DomainUtil.db.push(`/domains[${index}]`, domain, true);
|
||||
DomainUtil.reloadDB();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
ipcRenderer.on('enter-fullscreen', () => {
|
||||
@@ -582,6 +733,14 @@ class ServerManagerView {
|
||||
ipcRenderer.on('open-feedback-modal', () => {
|
||||
feedbackHolder.classList.add('show');
|
||||
});
|
||||
|
||||
ipcRenderer.on('copy-zulip-url', () => {
|
||||
clipboard.writeText(DomainUtil.getDomain(this.activeTabIndex).url);
|
||||
});
|
||||
|
||||
ipcRenderer.on('new-server', () => {
|
||||
this.openSettings('AddServer');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,8 @@ function parseReply(reply) {
|
||||
|
||||
function setupReply(id) {
|
||||
const { narrow } = window;
|
||||
narrow.by_subject(id, { trigger: 'notification' });
|
||||
const narrowByTopic = narrow.by_topic || narrow.by_subject;
|
||||
narrowByTopic(id, { trigger: 'notification' });
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -4,6 +4,7 @@ const {
|
||||
remote: { app }
|
||||
} = require('electron');
|
||||
|
||||
const params = require('../utils/params-util.js');
|
||||
const DefaultNotification = require('./default-notification');
|
||||
const { appId, loadBots } = require('./helpers');
|
||||
|
||||
@@ -19,9 +20,8 @@ if (process.platform === 'darwin') {
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
// Call this function only when user is logged in
|
||||
// eslint-disable-next-line no-undef, camelcase
|
||||
if (page_params.realm_uri) {
|
||||
if (params.isPageParams() && page_params.realm_uri) {
|
||||
loadBots();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use-strict';
|
||||
|
||||
const { dialog } = require('electron').remote;
|
||||
const path = require('path');
|
||||
|
||||
const BaseComponent = require(__dirname + '/../../components/base.js');
|
||||
const CertificateUtil = require(__dirname + '/../../utils/certificate-util.js');
|
||||
@@ -14,15 +15,15 @@ class AddCertificate extends BaseComponent {
|
||||
}
|
||||
|
||||
template() {
|
||||
return `
|
||||
<div class="settings-card server-center certificates-card">
|
||||
return `
|
||||
<div class="settings-card certificates-card">
|
||||
<div class="certificate-input">
|
||||
<div>Organization URL :</div>
|
||||
<div>Organization URL</div>
|
||||
<input class="setting-input-value" autofocus placeholder="your-organization.zulipchat.com or zulip.your-organization.com"/>
|
||||
</div>
|
||||
<div class="certificate-input">
|
||||
<div>Custom CA's certificate file :</div>
|
||||
<button id="add-certificate-button">Add</button>
|
||||
<div>Certificate file</div>
|
||||
<button class="green" id="add-certificate-button">Upload</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -41,7 +42,7 @@ class AddCertificate extends BaseComponent {
|
||||
const serverUrl = this.serverUrl.value;
|
||||
if (certificate !== '' && serverUrl !== '') {
|
||||
const server = encodeURIComponent(DomainUtil.formatUrl(serverUrl));
|
||||
const fileName = certificate.substring(certificate.lastIndexOf('/') + 1);
|
||||
const fileName = path.basename(certificate);
|
||||
const copy = CertificateUtil.copyCertificate(server, certificate, fileName);
|
||||
if (!copy) {
|
||||
return;
|
||||
|
||||
@@ -17,7 +17,7 @@ class ConnectedOrgSection extends BaseSection {
|
||||
<div class="page-title">Connected organizations</div>
|
||||
<div class="title" id="existing-servers">All the connected orgnizations will appear here.</div>
|
||||
<div id="server-info-container"></div>
|
||||
|
||||
<div id="new-org-button"><button class="green sea w-250">Connect to another organization</button></div>
|
||||
<div class="page-title">Add Custom Certificates</div>
|
||||
<div id="add-certificate-container"></div>
|
||||
</div>
|
||||
@@ -33,8 +33,11 @@ class ConnectedOrgSection extends BaseSection {
|
||||
|
||||
const servers = DomainUtil.getDomains();
|
||||
this.props.$root.innerHTML = this.template();
|
||||
|
||||
this.$serverInfoContainer = document.getElementById('server-info-container');
|
||||
this.$existingServers = document.getElementById('existing-servers');
|
||||
this.$newOrgButton = document.getElementById('new-org-button');
|
||||
this.$addCertificateContainer = document.getElementById('add-certificate-container');
|
||||
|
||||
const noServerText = 'All the connected orgnizations will appear here';
|
||||
// Show noServerText if no servers are there otherwise hide it
|
||||
@@ -49,7 +52,12 @@ class ConnectedOrgSection extends BaseSection {
|
||||
}).init();
|
||||
}
|
||||
|
||||
this.$addCertificateContainer = document.getElementById('add-certificate-container');
|
||||
this.$newOrgButton.addEventListener('click', () => {
|
||||
// We don't need to import this since it's already imported in other files
|
||||
// eslint-disable-next-line no-undef
|
||||
ipcRenderer.send('forward-message', 'open-org-tab');
|
||||
});
|
||||
|
||||
this.initAddCertificate();
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,12 @@ class GeneralSection extends BaseSection {
|
||||
<div class="setting-description">Show app icon in system tray</div>
|
||||
<div class="setting-control"></div>
|
||||
</div>
|
||||
<div class="setting-row" id="menubar-option" style= "display:${process.platform === 'darwin' ? 'none' : ''}">
|
||||
<div class="setting-description">Auto hide menu bar (Press Alt key to display)</div>
|
||||
<div class="setting-control"></div>
|
||||
</div>
|
||||
<div class="setting-row" id="sidebar-option">
|
||||
<div class="setting-description">Show sidebar (<span class="code">Cmd Or Ctrl+S</span>)</div>
|
||||
<div class="setting-description">Show sidebar (<span class="code">${process.platform === 'darwin' ? 'Cmd+Shift+S' : 'Ctrl+Shift+S'}</span>)</div>
|
||||
<div class="setting-control"></div>
|
||||
</div>
|
||||
<div class="setting-row" id="badge-option">
|
||||
@@ -40,10 +44,10 @@ class GeneralSection extends BaseSection {
|
||||
<div class="setting-control"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="title">Desktop Notification</div>
|
||||
<div class="title">Desktop Notifications</div>
|
||||
<div class="settings-card">
|
||||
<div class="setting-row" id="show-notification-option">
|
||||
<div class="setting-description">Show Desktop Notifications</div>
|
||||
<div class="setting-description">Show desktop notifications</div>
|
||||
<div class="setting-control"></div>
|
||||
</div>
|
||||
<div class="setting-row" id="silent-option">
|
||||
@@ -73,48 +77,54 @@ class GeneralSection extends BaseSection {
|
||||
<div class="setting-control"></div>
|
||||
</div>
|
||||
<div class="setting-row" id="enable-spellchecker-option">
|
||||
<div class="setting-description">Enable Spellchecker (requires restart)</div>
|
||||
<div class="setting-description">Enable spellchecker (requires restart)</div>
|
||||
<div class="setting-control"></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">Advanced</div>
|
||||
<div class="settings-card">
|
||||
<div class="setting-row" id="enable-error-reporting">
|
||||
<div class="setting-description">Enable error reporting (requires restart)</div>
|
||||
<div class="setting-control"></div>
|
||||
</div>
|
||||
<div class="setting-row" id="show-download-folder">
|
||||
<div class="setting-description">Show downloaded files in file manager</div>
|
||||
<div class="setting-control"></div>
|
||||
</div>
|
||||
<div class="setting-row" id="add-custom-css">
|
||||
<div class="setting-description">
|
||||
Add custom CSS
|
||||
</div>
|
||||
<button class="custom-css-button green">Upload</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 class="setting-row" id="download-folder">
|
||||
<div class="setting-description">
|
||||
Default download location
|
||||
</div>
|
||||
<button class="download-folder-button blue">Choose</button>
|
||||
<button class="download-folder-button green">Change</button>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-description">
|
||||
<div class="download-folder-path">${ConfigUtil.getConfigItem('downloadsPath', `${app.getPath('downloads')}`)}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="title">Reset Application Data</div>
|
||||
<div class="settings-card">
|
||||
<div class="setting-row" id="resetdata-option">
|
||||
<div class="setting-description">This will delete all application data including all added accounts and preferences
|
||||
</div>
|
||||
<button class="reset-data-button blue">Reset App Data</button>
|
||||
<button class="reset-data-button red w-150">Reset App Data</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -138,6 +148,8 @@ class GeneralSection extends BaseSection {
|
||||
this.showCustomCSSPath();
|
||||
this.removeCustomCSS();
|
||||
this.downloadFolder();
|
||||
this.showDownloadFolder();
|
||||
this.enableErrorReporting();
|
||||
|
||||
// Platform specific settings
|
||||
|
||||
@@ -149,6 +161,11 @@ class GeneralSection extends BaseSection {
|
||||
if (process.platform === 'darwin') {
|
||||
this.updateDockBouncing();
|
||||
}
|
||||
|
||||
// Auto hide menubar on Windows and Linux
|
||||
if (process.platform !== 'darwin') {
|
||||
this.updateMenubarOption();
|
||||
}
|
||||
}
|
||||
|
||||
updateTrayOption() {
|
||||
@@ -164,6 +181,19 @@ class GeneralSection extends BaseSection {
|
||||
});
|
||||
}
|
||||
|
||||
updateMenubarOption() {
|
||||
this.generateSettingOption({
|
||||
$element: document.querySelector('#menubar-option .setting-control'),
|
||||
value: ConfigUtil.getConfigItem('autoHideMenubar', false),
|
||||
clickHandler: () => {
|
||||
const newValue = !ConfigUtil.getConfigItem('autoHideMenubar');
|
||||
ConfigUtil.setConfigItem('autoHideMenubar', newValue);
|
||||
ipcRenderer.send('toggle-menubar', newValue);
|
||||
this.updateMenubarOption();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateBadgeOption() {
|
||||
this.generateSettingOption({
|
||||
$element: document.querySelector('#badge-option .setting-control'),
|
||||
@@ -208,6 +238,10 @@ class GeneralSection extends BaseSection {
|
||||
clickHandler: () => {
|
||||
const newValue = !ConfigUtil.getConfigItem('autoUpdate');
|
||||
ConfigUtil.setConfigItem('autoUpdate', newValue);
|
||||
if (!newValue) {
|
||||
ConfigUtil.setConfigItem('betaUpdate', false);
|
||||
this.betaUpdateOption();
|
||||
}
|
||||
this.autoUpdateOption();
|
||||
}
|
||||
});
|
||||
@@ -219,8 +253,10 @@ class GeneralSection extends BaseSection {
|
||||
value: ConfigUtil.getConfigItem('betaUpdate', false),
|
||||
clickHandler: () => {
|
||||
const newValue = !ConfigUtil.getConfigItem('betaUpdate');
|
||||
ConfigUtil.setConfigItem('betaUpdate', newValue);
|
||||
this.betaUpdateOption();
|
||||
if (ConfigUtil.getConfigItem('autoUpdate')) {
|
||||
ConfigUtil.setConfigItem('betaUpdate', newValue);
|
||||
this.betaUpdateOption();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -288,6 +324,18 @@ class GeneralSection extends BaseSection {
|
||||
});
|
||||
}
|
||||
|
||||
enableErrorReporting() {
|
||||
this.generateSettingOption({
|
||||
$element: document.querySelector('#enable-error-reporting .setting-control'),
|
||||
value: ConfigUtil.getConfigItem('errorReporting', true),
|
||||
clickHandler: () => {
|
||||
const newValue = !ConfigUtil.getConfigItem('errorReporting');
|
||||
ConfigUtil.setConfigItem('errorReporting', newValue);
|
||||
this.enableErrorReporting();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
clearAppDataDialog() {
|
||||
const clearAppDataMessage = 'By clicking proceed you will be removing all added accounts and preferences from Zulip. When the application restarts, it will be as if you are starting Zulip for the first time.';
|
||||
const getAppPath = path.join(app.getPath('appData'), app.getName());
|
||||
@@ -385,6 +433,18 @@ class GeneralSection extends BaseSection {
|
||||
});
|
||||
}
|
||||
|
||||
showDownloadFolder() {
|
||||
this.generateSettingOption({
|
||||
$element: document.querySelector('#show-download-folder .setting-control'),
|
||||
value: ConfigUtil.getConfigItem('showDownloadFolder', false),
|
||||
clickHandler: () => {
|
||||
const newValue = !ConfigUtil.getConfigItem('showDownloadFolder');
|
||||
ConfigUtil.setConfigItem('showDownloadFolder', newValue);
|
||||
this.showDownloadFolder();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = GeneralSection;
|
||||
|
||||
@@ -38,8 +38,7 @@ class NetworkSection extends BaseSection {
|
||||
<input class="setting-input-value" placeholder="e.g. foobar.com"/>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="action blue" id="proxy-save-action">
|
||||
<i class="material-icons">check_box</i>
|
||||
<div class="action green" id="proxy-save-action">
|
||||
<span>Save</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -100,7 +99,7 @@ class NetworkSection extends BaseSection {
|
||||
if (newValue === false) {
|
||||
// Remove proxy system proxy settings
|
||||
ConfigUtil.setConfigItem('proxyRules', '');
|
||||
ipcRenderer.send('forward-message', 'reload-proxy', true);
|
||||
ipcRenderer.send('forward-message', 'reload-proxy', false);
|
||||
}
|
||||
ConfigUtil.setConfigItem('useSystemProxy', newValue);
|
||||
this.updateProxyOption();
|
||||
|
||||
@@ -90,7 +90,7 @@ class PreferenceView extends BaseComponent {
|
||||
this.handleNavigation(navItem);
|
||||
});
|
||||
|
||||
ipcRenderer.on('toggle-sidebar', (event, state) => {
|
||||
ipcRenderer.on('toggle-sidebar-setting', (event, state) => {
|
||||
this.handleToggle('sidebar-option', state);
|
||||
});
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ class ShortcutsSection extends BaseSection {
|
||||
|
||||
return `
|
||||
<div class="settings-pane">
|
||||
<div class="settings-card tip"><p><b><i class="material-icons md-14">settings</i>Tip: </b>These desktop app shortcuts extend the Zulip webapp's <span id="open-hotkeys-link"> keyboard shortcuts</span>.</p></div>
|
||||
<div class="title">Application Shortcuts</div>
|
||||
<div class="settings-card">
|
||||
<table>
|
||||
@@ -129,6 +130,14 @@ class ShortcutsSection extends BaseSection {
|
||||
<td><kbd>Option</kbd><kbd>${userOSKey}</kbd><kbd>U</kbd></td>
|
||||
<td>Toggle DevTools for Active Tab</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Ctrl</kbd> + <kbd>Tab</kbd></td>
|
||||
<td>Switch to Next Organization</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>Tab</kbd></td>
|
||||
<td>Switch to Previous Organization</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="setting-control"></div>
|
||||
</div>
|
||||
@@ -160,7 +169,6 @@ class ShortcutsSection extends BaseSection {
|
||||
</table>
|
||||
<div class="setting-control"></div>
|
||||
</div>
|
||||
<div class="settings-card tip"><b><i class="material-icons md-14">settings</i>Tip: </b>These desktop app shortcuts extend the Zulip webapp's <span id="open-hotkeys-link">keyboard shortcuts</span>.</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -170,6 +178,7 @@ class ShortcutsSection extends BaseSection {
|
||||
|
||||
return `
|
||||
<div class="settings-pane">
|
||||
<div class="settings-card tip"><p><b><i class="material-icons md-14">settings</i>Tip: </b>These desktop app shortcuts extend the Zulip webapp's <span id="open-hotkeys-link"> keyboard shortcuts</span>.</p></div>
|
||||
<div class="title">Application Shortcuts</div>
|
||||
<div class="settings-card">
|
||||
<table>
|
||||
@@ -269,6 +278,14 @@ class ShortcutsSection extends BaseSection {
|
||||
<td><kbd>${userOSKey}</kbd> + <kbd>Shift</kbd> + <kbd>U</kbd></td>
|
||||
<td>Toggle DevTools for Active Tab</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>${userOSKey}</kbd> + <kbd>Tab</kbd></td>
|
||||
<td>Switch to Next Organization</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>${userOSKey}</kbd> + <kbd>Shift</kbd> + <kbd>Tab</kbd></td>
|
||||
<td>Switch to Previous Organization</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="setting-control"></div>
|
||||
</div>
|
||||
@@ -300,7 +317,6 @@ class ShortcutsSection extends BaseSection {
|
||||
</table>
|
||||
<div class="setting-control"></div>
|
||||
</div>
|
||||
<div class="tip"><b><i class="material-icons md-14">lightbulb_outline</i>Tip: </b>These desktop app shortcuts extend the Zulip webapp's <span id="open-hotkeys-link">keyboard shortcuts</span>.</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ const SetupSpellChecker = require('./spellchecker');
|
||||
|
||||
const ConfigUtil = require(__dirname + '/utils/config-util.js');
|
||||
const LinkUtil = require(__dirname + '/utils/link-util.js');
|
||||
const params = require(__dirname + '/utils/params-util.js');
|
||||
|
||||
// eslint-disable-next-line import/no-unassigned-import
|
||||
require('./notification');
|
||||
@@ -12,6 +13,9 @@ require('./notification');
|
||||
// Prevent drag and drop event in main process which prevents remote code executaion
|
||||
require(__dirname + '/shared/preventdrag.js');
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
window.electron_bridge = require('./electron-bridge');
|
||||
|
||||
const logout = () => {
|
||||
// Create the menu for the below
|
||||
document.querySelector('.dropdown-toggle').click();
|
||||
@@ -39,46 +43,44 @@ process.once('loaded', () => {
|
||||
|
||||
// To prevent failing this script on linux we need to load it after the document loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
if (params.isPageParams()) {
|
||||
// Get the default language of the server
|
||||
const serverLanguage = page_params.default_language; // eslint-disable-line no-undef, camelcase
|
||||
const serverLanguage = page_params.default_language; // eslint-disable-line no-undef, camelcase
|
||||
if (serverLanguage) {
|
||||
// Set spellcheker language
|
||||
ConfigUtil.setConfigItem('spellcheckerLanguage', serverLanguage);
|
||||
// Init spellchecker
|
||||
SetupSpellChecker.init();
|
||||
}
|
||||
// redirect users to network troubleshooting page
|
||||
const getRestartButton = document.querySelector('.restart_get_events_button');
|
||||
if (getRestartButton) {
|
||||
getRestartButton.addEventListener('click', () => {
|
||||
ipcRenderer.send('forward-message', 'reload-viewer');
|
||||
});
|
||||
}
|
||||
// Open image attachment link in the lightbox instead of opening in the default browser
|
||||
const { $, lightbox } = window;
|
||||
$('#main_div').on('click', '.message_content p a', function (e) {
|
||||
const url = $(this).attr('href');
|
||||
|
||||
if (serverLanguage) {
|
||||
// Set spellcheker language
|
||||
ConfigUtil.setConfigItem('spellcheckerLanguage', serverLanguage);
|
||||
// Init spellchecker
|
||||
SetupSpellChecker.init();
|
||||
}
|
||||
if (LinkUtil.isImage(url)) {
|
||||
const $img = $(this).parent().siblings('.message_inline_image').find('img');
|
||||
|
||||
// redirect users to network troubleshooting page
|
||||
const getRestartButton = document.querySelector('.restart_get_events_button');
|
||||
if (getRestartButton) {
|
||||
getRestartButton.addEventListener('click', () => {
|
||||
ipcRenderer.send('forward-message', 'reload-viewer');
|
||||
// prevent the image link from opening in a new page.
|
||||
e.preventDefault();
|
||||
// prevent the message compose dialog from happening.
|
||||
e.stopPropagation();
|
||||
|
||||
// Open image in the default browser if image preview is unavailable
|
||||
if (!$img[0]) {
|
||||
shell.openExternal(window.location.origin + url);
|
||||
}
|
||||
// Open image in lightbox
|
||||
lightbox.open($img);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Open image attachment link in the lightbox instead of opening in the default browser
|
||||
const { $, lightbox } = window;
|
||||
|
||||
$('#main_div').on('click', '.message_content p a', function (e) {
|
||||
const url = $(this).attr('href');
|
||||
|
||||
if (LinkUtil.isImage(url)) {
|
||||
const $img = $(this).parent().siblings('.message_inline_image').find('img');
|
||||
|
||||
// prevent the image link from opening in a new page.
|
||||
e.preventDefault();
|
||||
// prevent the message compose dialog from happening.
|
||||
e.stopPropagation();
|
||||
|
||||
// Open image in the default browser if image preview is unavailable
|
||||
if (!$img[0]) {
|
||||
shell.openExternal(window.location.origin + url);
|
||||
}
|
||||
// Open image in lightbox
|
||||
lightbox.open($img);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Clean up spellchecker events after you navigate away from this page;
|
||||
@@ -89,8 +91,15 @@ window.addEventListener('beforeunload', () => {
|
||||
|
||||
// electron's globalShortcut can cause unexpected results
|
||||
// so adding the reload shortcut in the old-school way
|
||||
// Zoom from numpad keys is not supported by electron, so adding it through listeners.
|
||||
document.addEventListener('keydown', event => {
|
||||
if (event.code === 'F5') {
|
||||
ipcRenderer.send('forward-message', 'hard-reload');
|
||||
} else if (event.ctrlKey && event.code === 'NumpadAdd') {
|
||||
ipcRenderer.send('forward-message', 'zoomIn');
|
||||
} else if (event.ctrlKey && event.code === 'NumpadSubtract') {
|
||||
ipcRenderer.send('forward-message', 'zoomOut');
|
||||
} else if (event.ctrlKey && event.code === 'Numpad0') {
|
||||
ipcRenderer.send('forward-message', 'zoomActualSize');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -32,14 +32,7 @@ class SetupSpellChecker {
|
||||
|
||||
const userLanguage = ConfigUtil.getConfigItem('spellcheckerLanguage');
|
||||
|
||||
// eslint-disable-next-line no-unused-expressions
|
||||
process.platform === 'darwin' ?
|
||||
// On macOS, spellchecker fails to auto-detect the lanugage user is typing in
|
||||
// that's why we need to mention it explicitly
|
||||
this.SpellCheckHandler.switchLanguage(userLanguage) :
|
||||
// On Linux and Windows, spellchecker can automatically detects the language the user is typing in
|
||||
// and silently switches on the fly; thus we can start off as US English
|
||||
this.SpellCheckHandler.switchLanguage('en-US');
|
||||
this.SpellCheckHandler.switchLanguage(userLanguage);
|
||||
}
|
||||
|
||||
const contextMenuBuilder = new ContextMenuBuilder(this.SpellCheckHandler);
|
||||
|
||||
@@ -3,9 +3,9 @@ const path = require('path');
|
||||
|
||||
const electron = require('electron');
|
||||
|
||||
const {ipcRenderer, remote} = electron;
|
||||
const { ipcRenderer, remote } = electron;
|
||||
|
||||
const {Tray, Menu, nativeImage, BrowserWindow} = remote;
|
||||
const { Tray, Menu, nativeImage, BrowserWindow } = remote;
|
||||
|
||||
const APP_ICON = path.join(__dirname, '../../resources/tray', 'tray');
|
||||
|
||||
@@ -99,7 +99,9 @@ const renderNativeImage = function (arg) {
|
||||
.then(() => renderCanvas(arg))
|
||||
.then(canvas => {
|
||||
const pngData = nativeImage.createFromDataURL(canvas.toDataURL('image/png')).toPNG();
|
||||
return Promise.resolve(nativeImage.createFromBuffer(pngData, config.pixelRatio));
|
||||
return Promise.resolve(nativeImage.createFromBuffer(pngData, {
|
||||
scaleFactor: config.pixelRatio
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -115,50 +117,36 @@ function sendAction(action) {
|
||||
|
||||
const createTray = function () {
|
||||
window.tray = new Tray(iconPath());
|
||||
const contextMenu = Menu.buildFromTemplate([{
|
||||
label: 'About',
|
||||
click() {
|
||||
// We need to focus the main window first
|
||||
ipcRenderer.send('focus-app');
|
||||
sendAction('open-about');
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: 'Zulip',
|
||||
click() {
|
||||
ipcRenderer.send('focus-app');
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
click() {
|
||||
ipcRenderer.send('focus-app');
|
||||
sendAction('open-settings');
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Quit',
|
||||
click() {
|
||||
ipcRenderer.send('quit-app');
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Focus',
|
||||
click() {
|
||||
ipcRenderer.send('focus-app');
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
click() {
|
||||
ipcRenderer.send('focus-app');
|
||||
sendAction('open-settings');
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'separator'
|
||||
},
|
||||
{
|
||||
label: 'Quit',
|
||||
click() {
|
||||
ipcRenderer.send('quit-app');
|
||||
}
|
||||
}
|
||||
]);
|
||||
window.tray.setContextMenu(contextMenu);
|
||||
window.tray.on('click', () => {
|
||||
// Click event only works on Windows
|
||||
if (process.platform === 'win32') {
|
||||
if (process.platform === 'linux' || process.platform === 'win32') {
|
||||
window.tray.on('click', () => {
|
||||
ipcRenderer.send('toggle-app');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
ipcRenderer.on('destroytray', event => {
|
||||
|
||||
25
app/renderer/js/utils/common-util.js
Normal file
25
app/renderer/js/utils/common-util.js
Normal file
@@ -0,0 +1,25 @@
|
||||
'use strict';
|
||||
|
||||
let instance = null;
|
||||
|
||||
class CommonUtil {
|
||||
constructor() {
|
||||
if (instance) {
|
||||
return instance;
|
||||
} else {
|
||||
instance = this;
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
// unescape already encoded/escaped strings
|
||||
decodeString(string) {
|
||||
const parser = new DOMParser();
|
||||
const dom = parser.parseFromString(
|
||||
'<!doctype html><body>' + string,
|
||||
'text/html');
|
||||
return dom.body.textContent;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new CommonUtil();
|
||||
@@ -39,7 +39,12 @@ class ConfigUtil {
|
||||
}
|
||||
|
||||
getConfigItem(key, defaultValue = null) {
|
||||
this.reloadDB();
|
||||
try {
|
||||
this.db.reload();
|
||||
} catch (err) {
|
||||
logger.error('Error while reloading settings.json: ');
|
||||
logger.error(err);
|
||||
}
|
||||
const value = this.db.getData('/')[key];
|
||||
if (value === undefined) {
|
||||
this.setConfigItem(key, defaultValue);
|
||||
@@ -50,19 +55,24 @@ class ConfigUtil {
|
||||
}
|
||||
// This function returns whether a key exists in the configuration file (settings.json)
|
||||
isConfigItemExists(key) {
|
||||
this.reloadDB();
|
||||
try {
|
||||
this.db.reload();
|
||||
} catch (err) {
|
||||
logger.error('Error while reloading settings.json: ');
|
||||
logger.error(err);
|
||||
}
|
||||
const value = this.db.getData('/')[key];
|
||||
return (value !== undefined);
|
||||
}
|
||||
|
||||
setConfigItem(key, value) {
|
||||
this.db.push(`/${key}`, value, true);
|
||||
this.reloadDB();
|
||||
this.db.save();
|
||||
}
|
||||
|
||||
removeConfigItem(key) {
|
||||
this.db.delete(`/${key}`);
|
||||
this.reloadDB();
|
||||
this.db.save();
|
||||
}
|
||||
|
||||
reloadDB() {
|
||||
|
||||
@@ -9,7 +9,7 @@ const escape = require('escape-html');
|
||||
|
||||
const Logger = require('./logger-util');
|
||||
|
||||
const CertificateUtil = require(__dirname + '/certificate-util.js');
|
||||
const RequestUtil = require(__dirname + '/../utils/request-util.js');
|
||||
|
||||
const logger = new Logger({
|
||||
file: `domain-util.log`,
|
||||
@@ -61,9 +61,10 @@ class DomainUtil {
|
||||
}
|
||||
|
||||
addDomain(server) {
|
||||
const ignoreCerts = server.ignoreCerts;
|
||||
return new Promise(resolve => {
|
||||
if (server.icon) {
|
||||
this.saveServerIcon(server.icon).then(localIconUrl => {
|
||||
this.saveServerIcon(server, ignoreCerts).then(localIconUrl => {
|
||||
server.icon = localIconUrl;
|
||||
this.db.push('/domains[]', server, true);
|
||||
this.reloadDB();
|
||||
@@ -100,32 +101,25 @@ class DomainUtil {
|
||||
return false;
|
||||
}
|
||||
|
||||
checkDomain(domain, silent = false) {
|
||||
// ignoreCerts parameter helps in fetching server icon and
|
||||
// other server details when user chooses to ignore certificate warnings
|
||||
checkDomain(domain, ignoreCerts = false, silent = false) {
|
||||
if (!silent && this.duplicateDomain(domain)) {
|
||||
// Do not check duplicate in silent mode
|
||||
return Promise.reject('This server has been added.');
|
||||
}
|
||||
|
||||
domain = this.formatUrl(domain);
|
||||
|
||||
const certificate = CertificateUtil.getCertificate(encodeURIComponent(domain));
|
||||
let certificateLocation = '';
|
||||
|
||||
if (certificate) {
|
||||
// To handle case where certificate has been moved from the location in certificates.json
|
||||
try {
|
||||
certificateLocation = fs.readFileSync(certificate);
|
||||
} catch (err) {
|
||||
logger.warn('Error while trying to get certificate: ' + err);
|
||||
}
|
||||
}
|
||||
// If certificate for the domain exists add it as a ca key in the request's parameter else consider only domain as the parameter for request
|
||||
const checkDomain = (certificateLocation) ? ({url: domain + '/static/audio/zulip.ogg', ca: certificateLocation}) : domain + '/static/audio/zulip.ogg';
|
||||
const checkDomain = {
|
||||
url: domain + '/static/audio/zulip.ogg',
|
||||
...RequestUtil.requestOptions(domain, ignoreCerts)
|
||||
};
|
||||
|
||||
const serverConf = {
|
||||
icon: defaultIconUrl,
|
||||
url: domain,
|
||||
alias: domain
|
||||
alias: domain,
|
||||
ignoreCerts
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -142,14 +136,14 @@ class DomainUtil {
|
||||
const certsError = error.toString().includes('certificate');
|
||||
if (!error && response.statusCode < 400) {
|
||||
// Correct
|
||||
this.getServerSettings(domain).then(serverSettings => {
|
||||
this.getServerSettings(domain, serverConf.ignoreCerts).then(serverSettings => {
|
||||
resolve(serverSettings);
|
||||
}, () => {
|
||||
resolve(serverConf);
|
||||
});
|
||||
} else if (domain.indexOf(whitelistDomains) >= 0 || certsError) {
|
||||
if (silent) {
|
||||
this.getServerSettings(domain).then(serverSettings => {
|
||||
this.getServerSettings(domain, serverConf.ignoreCerts).then(serverSettings => {
|
||||
resolve(serverSettings);
|
||||
}, () => {
|
||||
resolve(serverConf);
|
||||
@@ -172,7 +166,9 @@ class DomainUtil {
|
||||
detail: certErrorDetail
|
||||
}, response => {
|
||||
if (response === 0) {
|
||||
this.getServerSettings(domain).then(serverSettings => {
|
||||
// set ignoreCerts parameter to true in case user responds with yes
|
||||
serverConf.ignoreCerts = true;
|
||||
this.getServerSettings(domain, serverConf.ignoreCerts).then(serverSettings => {
|
||||
resolve(serverSettings);
|
||||
}, () => {
|
||||
resolve(serverConf);
|
||||
@@ -192,10 +188,14 @@ class DomainUtil {
|
||||
});
|
||||
}
|
||||
|
||||
getServerSettings(domain) {
|
||||
const serverSettingsUrl = domain + '/api/v1/server_settings';
|
||||
getServerSettings(domain, ignoreCerts = false) {
|
||||
const serverSettingsOptions = {
|
||||
url: domain + '/api/v1/server_settings',
|
||||
...RequestUtil.requestOptions(domain, ignoreCerts)
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
request(serverSettingsUrl, (error, response) => {
|
||||
request(serverSettingsOptions, (error, response) => {
|
||||
if (!error && response.statusCode === 200) {
|
||||
const data = JSON.parse(response.body);
|
||||
if (data.hasOwnProperty('realm_icon') && data.realm_icon) {
|
||||
@@ -204,7 +204,8 @@ class DomainUtil {
|
||||
// Following check handles both the cases
|
||||
icon: data.realm_icon.startsWith('/') ? data.realm_uri + data.realm_icon : data.realm_icon,
|
||||
url: data.realm_uri,
|
||||
alias: escape(data.realm_name)
|
||||
alias: escape(data.realm_name),
|
||||
ignoreCerts
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -214,13 +215,21 @@ class DomainUtil {
|
||||
});
|
||||
}
|
||||
|
||||
saveServerIcon(url) {
|
||||
saveServerIcon(server, ignoreCerts = false) {
|
||||
const url = server.icon;
|
||||
const domain = server.url;
|
||||
|
||||
const serverIconOptions = {
|
||||
url,
|
||||
...RequestUtil.requestOptions(domain, ignoreCerts)
|
||||
};
|
||||
|
||||
// The save will always succeed. If url is invalid, downgrade to default icon.
|
||||
return new Promise(resolve => {
|
||||
const filePath = this.generateFilePath(url);
|
||||
const file = fs.createWriteStream(filePath);
|
||||
try {
|
||||
request(url).on('response', response => {
|
||||
request(serverIconOptions).on('response', response => {
|
||||
response.on('error', err => {
|
||||
logger.log('Could not get server icon.');
|
||||
logger.log(err);
|
||||
@@ -247,8 +256,9 @@ class DomainUtil {
|
||||
|
||||
updateSavedServer(url, index) {
|
||||
// Does not promise successful update
|
||||
this.checkDomain(url, true).then(newServerConf => {
|
||||
this.saveServerIcon(newServerConf.icon).then(localIconUrl => {
|
||||
const ignoreCerts = this.getDomain(index).ignoreCerts;
|
||||
this.checkDomain(url, ignoreCerts, true).then(newServerConf => {
|
||||
this.saveServerIcon(newServerConf, ignoreCerts).then(localIconUrl => {
|
||||
newServerConf.icon = localIconUrl;
|
||||
this.updateDomain(index, newServerConf);
|
||||
this.reloadDB();
|
||||
|
||||
@@ -34,6 +34,12 @@ class LinkUtil {
|
||||
const isImageUrl = /\.(bmp|gif|jpg|jpeg|png|webp)\?*.*$/i;
|
||||
return isImageUrl.test(url);
|
||||
}
|
||||
|
||||
isPDF(url) {
|
||||
// test for pdf extension
|
||||
const isPDFUrl = /\.(pdf)\?*.*$/i;
|
||||
return isPDFUrl.test(url);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new LinkUtil();
|
||||
|
||||
@@ -1,14 +1,28 @@
|
||||
const NodeConsole = require('console').Console;
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const isDev = require('electron-is-dev');
|
||||
const { initSetUp } = require('./default-util');
|
||||
const { sentryInit, captureException } = require('./sentry-util');
|
||||
|
||||
initSetUp();
|
||||
sentryInit();
|
||||
|
||||
let app = null;
|
||||
let reportErrors = true;
|
||||
if (process.type === 'renderer') {
|
||||
app = require('electron').remote.app;
|
||||
|
||||
// Report Errors to Sentry only if it is enabled in settings
|
||||
// Gets the value of reportErrors from config-util for renderer process
|
||||
// For main process, sentryInit() is handled in index.js
|
||||
const { ipcRenderer } = require('electron');
|
||||
ipcRenderer.send('error-reporting');
|
||||
ipcRenderer.on('error-reporting-val', (event, errorReporting) => {
|
||||
reportErrors = errorReporting;
|
||||
if (reportErrors) {
|
||||
sentryInit();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
app = require('electron').app;
|
||||
}
|
||||
@@ -30,6 +44,13 @@ class Logger {
|
||||
timestamp = this.getTimestamp;
|
||||
}
|
||||
|
||||
// Trim log according to type of process
|
||||
if (process.type === 'renderer') {
|
||||
requestIdleCallback(() => this.trimLog(file));
|
||||
} else {
|
||||
process.nextTick(() => this.trimLog(file));
|
||||
}
|
||||
|
||||
const fileStream = fs.createWriteStream(file, { flags: 'a' });
|
||||
const nodeConsole = new NodeConsole(fileStream);
|
||||
|
||||
@@ -86,7 +107,27 @@ class Logger {
|
||||
}
|
||||
|
||||
reportSentry(err) {
|
||||
captureException(err);
|
||||
if (reportErrors) {
|
||||
captureException(err);
|
||||
}
|
||||
}
|
||||
|
||||
trimLog(file) {
|
||||
fs.readFile(file, 'utf8', (err, data) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
const MAX_LOG_FILE_LINES = 500;
|
||||
const logs = data.split(os.EOL);
|
||||
const logLength = logs.length - 1;
|
||||
|
||||
// Keep bottom MAX_LOG_FILE_LINES of each log instance
|
||||
if (logLength > MAX_LOG_FILE_LINES) {
|
||||
const trimmedLogs = logs.slice(logLength - MAX_LOG_FILE_LINES);
|
||||
const toWrite = trimmedLogs.join(os.EOL);
|
||||
fs.writeFileSync(file, toWrite);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
15
app/renderer/js/utils/params-util.js
Normal file
15
app/renderer/js/utils/params-util.js
Normal file
@@ -0,0 +1,15 @@
|
||||
// This util function returns the page params if they're present else returns null
|
||||
function isPageParams() {
|
||||
let webpageParams = null;
|
||||
try {
|
||||
// eslint-disable-next-line no-undef, camelcase
|
||||
webpageParams = page_params;
|
||||
} catch (err) {
|
||||
webpageParams = null;
|
||||
}
|
||||
return webpageParams;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isPageParams
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const url = require('url');
|
||||
const ConfigUtil = require('./config-util.js');
|
||||
|
||||
let instance = null;
|
||||
@@ -15,10 +16,43 @@ class ProxyUtil {
|
||||
return instance;
|
||||
}
|
||||
|
||||
// Return proxy to be used for a particular uri, to be used for request
|
||||
getProxy(uri) {
|
||||
uri = url.parse(uri);
|
||||
const proxyRules = ConfigUtil.getConfigItem('proxyRules', '').split(';');
|
||||
// If SPS is on and system uses no proxy then request should not try to use proxy from
|
||||
// environment. NO_PROXY = '*' makes request ignore all environment proxy variables.
|
||||
if (proxyRules[0] === '') {
|
||||
process.env.NO_PROXY = '*';
|
||||
return;
|
||||
}
|
||||
|
||||
const proxyRule = {};
|
||||
if (uri.protocol === 'http:') {
|
||||
proxyRules.forEach(proxy => {
|
||||
if (proxy.includes('http=')) {
|
||||
proxyRule.hostname = proxy.split('http=')[1].trim().split(':')[0];
|
||||
proxyRule.port = proxy.split('http=')[1].trim().split(':')[1];
|
||||
}
|
||||
});
|
||||
return proxyRule;
|
||||
}
|
||||
|
||||
if (uri.protocol === 'https:') {
|
||||
proxyRules.forEach(proxy => {
|
||||
if (proxy.includes('https=')) {
|
||||
proxyRule.hostname = proxy.split('https=')[1].trim().split(':')[0];
|
||||
proxyRule.port = proxy.split('https=')[1].trim().split(':')[1];
|
||||
}
|
||||
});
|
||||
return proxyRule;
|
||||
}
|
||||
}
|
||||
|
||||
resolveSystemProxy(mainWindow) {
|
||||
const page = mainWindow.webContents;
|
||||
const ses = page.session;
|
||||
const resolveProxyUrl = 'www.google.com';
|
||||
const resolveProxyUrl = 'www.example.com';
|
||||
|
||||
// Check HTTP Proxy
|
||||
const httpProxy = new Promise(resolve => {
|
||||
|
||||
@@ -45,8 +45,8 @@ class ReconnectUtil {
|
||||
const errMsgHolder = document.querySelector('#description');
|
||||
if (errMsgHolder) {
|
||||
errMsgHolder.innerHTML = `
|
||||
<div>You internet connection does't seem to work properly!</div>
|
||||
</div>Verify that it works and then click try again.</div>`;
|
||||
<div>Your internet connection doesn't seem to work properly!</div>
|
||||
<div>Verify that it works and then click try again.</div>`;
|
||||
}
|
||||
return resolve(false);
|
||||
});
|
||||
|
||||
62
app/renderer/js/utils/request-util.js
Normal file
62
app/renderer/js/utils/request-util.js
Normal file
@@ -0,0 +1,62 @@
|
||||
const fs = require('fs');
|
||||
const Logger = require('./logger-util');
|
||||
|
||||
const CertificateUtil = require(__dirname + '/certificate-util.js');
|
||||
const ProxyUtil = require(__dirname + '/proxy-util.js');
|
||||
const ConfigUtil = require(__dirname + '/config-util.js');
|
||||
const SystemUtil = require(__dirname + '/../utils/system-util.js');
|
||||
|
||||
const logger = new Logger({
|
||||
file: `request-util.log`,
|
||||
timestamp: true
|
||||
});
|
||||
|
||||
let instance = null;
|
||||
|
||||
class RequestUtil {
|
||||
constructor() {
|
||||
if (!instance) {
|
||||
instance = this;
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
// ignoreCerts parameter helps in fetching server icon and
|
||||
// other server details when user chooses to ignore certificate warnings
|
||||
requestOptions(domain, ignoreCerts) {
|
||||
domain = this.formatUrl(domain);
|
||||
const certificate = CertificateUtil.getCertificate(
|
||||
encodeURIComponent(domain)
|
||||
);
|
||||
let certificateLocation = '';
|
||||
if (certificate) {
|
||||
// To handle case where certificate has been moved from the location in certificates.json
|
||||
try {
|
||||
certificateLocation = fs.readFileSync(certificate);
|
||||
} catch (err) {
|
||||
logger.warn('Error while trying to get certificate: ' + err);
|
||||
}
|
||||
}
|
||||
const proxyEnabled = ConfigUtil.getConfigItem('useManualProxy') || ConfigUtil.getConfigItem('useSystemProxy');
|
||||
// If certificate for the domain exists add it as a ca key in the request's parameter else consider only domain as the parameter for request
|
||||
// Add proxy as a parameter if it is being used.
|
||||
return {
|
||||
ca: certificateLocation ? certificateLocation : '',
|
||||
proxy: proxyEnabled ? ProxyUtil.getProxy(domain) : '',
|
||||
ecdhCurve: 'auto',
|
||||
headers: { 'User-Agent': SystemUtil.getUserAgent() },
|
||||
rejectUnauthorized: !ignoreCerts
|
||||
};
|
||||
}
|
||||
|
||||
formatUrl(domain) {
|
||||
const hasPrefix = (domain.indexOf('http') === 0);
|
||||
if (hasPrefix) {
|
||||
return domain;
|
||||
} else {
|
||||
return (domain.indexOf('localhost:') >= 0) ? `http://${domain}` : `https://${domain}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new RequestUtil();
|
||||
@@ -5,6 +5,10 @@ const sentryInit = () => {
|
||||
if (!isDev) {
|
||||
init({
|
||||
dsn: 'SENTRY_DSN',
|
||||
// We should ignore this error since it's harmless and we know the reason behind this
|
||||
// This error mainly comes from the console logs.
|
||||
// This is a temp solution until Sentry supports disabling the console logs
|
||||
ignoreErrors: ['does not appear to be a valid Zulip server'],
|
||||
sendTimeout: 30 // wait 30 seconds before considering the sending capture to have failed, default is 1 second
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,56 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="responsive desktop">
|
||||
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Zulip</title>
|
||||
<link rel="stylesheet" href="css/main.css" type="text/css" media="screen">
|
||||
</head>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Zulip</title>
|
||||
<link rel="stylesheet" href="css/main.css" type="text/css" media="screen">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="content">
|
||||
<div class="popup">
|
||||
<span class="popuptext hidden" id="fullscreen-popup"></span>
|
||||
</div>
|
||||
<div id="sidebar" class="toggle-sidebar">
|
||||
<div id="view-controls-container">
|
||||
<div id="tabs-container"></div>
|
||||
<div id="add-tab" class="tab functional-tab">
|
||||
<div class="server-tab" id="add-action">
|
||||
<i class="material-icons">add</i>
|
||||
<body>
|
||||
<div id="content">
|
||||
<div class="popup">
|
||||
<span class="popuptext hidden" id="fullscreen-popup"></span>
|
||||
</div>
|
||||
<div id="sidebar" class="toggle-sidebar">
|
||||
<div id="view-controls-container">
|
||||
<div id="tabs-container"></div>
|
||||
<div id="add-tab" class="tab functional-tab">
|
||||
<div class="server-tab" id="add-action">
|
||||
<i class="material-icons">add</i>
|
||||
</div>
|
||||
<span id="add-server-tooltip" style="display:none">Add organization</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="actions-container">
|
||||
<div class="action-button" id="dnd-action">
|
||||
<i class="material-icons md-48">notifications</i>
|
||||
<span id="dnd-tooltip" style="display:none">Do Not Disturb</span>
|
||||
</div>
|
||||
<div class="action-button" id="reload-action">
|
||||
<i class="material-icons md-48">refresh</i>
|
||||
<span id="reload-tooltip" style="display:none">Reload</span>
|
||||
</div>
|
||||
<div class="action-button disable" id="back-action">
|
||||
<i class="material-icons md-48">arrow_back</i>
|
||||
<span id="back-tooltip" style="display:none">Go Back</span>
|
||||
</div>
|
||||
<div class="action-button" id="settings-action">
|
||||
<i class="material-icons md-48">settings</i>
|
||||
<span id="setting-tooltip" style="display:none">Settings</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main-container">
|
||||
<div id="webviews-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
<span id="add-server-tooltip" style="display:none">Add organization</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="actions-container">
|
||||
<div class="action-button" id="dnd-action">
|
||||
<i class="material-icons md-48">notifications</i>
|
||||
<span id="dnd-tooltip" style="display:none">Do Not Disturb</span>
|
||||
</div>
|
||||
<div class="action-button" id="reload-action">
|
||||
<i class="material-icons md-48">refresh</i>
|
||||
<span id="reload-tooltip" style="display:none">Reload</span>
|
||||
</div>
|
||||
<div class="action-button disable" id="back-action">
|
||||
<i class="material-icons md-48">arrow_back</i>
|
||||
<span id="back-tooltip" style="display:none">Go Back</span>
|
||||
</div>
|
||||
<div class="action-button" id="settings-action">
|
||||
<i class="material-icons md-48">settings</i>
|
||||
<span id="setting-tooltip" style="display:none">Settings</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="main-container">
|
||||
<div id="webviews-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="feedback-modal">
|
||||
<send-feedback></send-feedback>
|
||||
</div>
|
||||
</body>
|
||||
<script src="js/main.js"></script>
|
||||
<script>require('./js/shared/preventdrag.js')</script>
|
||||
<div id="feedback-modal">
|
||||
<send-feedback></send-feedback>
|
||||
</div>
|
||||
</body>
|
||||
<script src="js/main.js"></script>
|
||||
<script>require('./js/shared/preventdrag.js')</script>
|
||||
</html>
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="responsive desktop">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Zulip - Network Troubleshooting</title>
|
||||
<link rel="stylesheet" href="css/network.css" type="text/css" media="screen">
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
<div id="picture"><img src="img/zulip_network.png"></div>
|
||||
<div id="title">Zulip can't connect</div>
|
||||
<div id="description">
|
||||
<div>Your computer seems to be offline.</div>
|
||||
<div>We will keep trying to reconnect, or you can try now.</div>
|
||||
</div>
|
||||
<div id="reconnect">Try now</div>
|
||||
</div>
|
||||
</body>
|
||||
<script src="js/pages/network.js"></script>
|
||||
<script>require('./js/shared/preventdrag.js')</script>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Zulip - Network Troubleshooting</title>
|
||||
<link rel="stylesheet" href="css/network.css" type="text/css" media="screen">
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
<div id="picture"><img src="img/zulip_network.png"></div>
|
||||
<div id="title">Zulip can't connect</div>
|
||||
<div id="description">
|
||||
<div>Your computer seems to be offline.</div>
|
||||
<div>We will keep trying to reconnect, or you can try now.</div>
|
||||
</div>
|
||||
<div id="reconnect">Try now</div>
|
||||
</div>
|
||||
</body>
|
||||
<script src="js/pages/network.js"></script>
|
||||
<script>require('./js/shared/preventdrag.js')</script>
|
||||
</html>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="responsive desktop">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Zulip - Settings</title>
|
||||
<link rel="stylesheet" href="css/preference.css" type="text/css" media="screen">
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
<div id="sidebar"></div>
|
||||
<div id="settings-container"></div>
|
||||
</div>
|
||||
</body>
|
||||
<script src="js/pages/preference/preference.js"></script>
|
||||
<script>require('./js/shared/preventdrag.js')</script>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Zulip - Settings</title>
|
||||
<link rel="stylesheet" href="css/preference.css" type="text/css" media="screen">
|
||||
</head>
|
||||
<body>
|
||||
<div id="content">
|
||||
<div id="sidebar"></div>
|
||||
<div id="settings-container"></div>
|
||||
</div>
|
||||
</body>
|
||||
<script src="js/pages/preference/preference.js"></script>
|
||||
<script>require('./js/shared/preventdrag.js')</script>
|
||||
</html>
|
||||
|
||||
BIN
build/icon.png
BIN
build/icon.png
Binary file not shown.
|
Before Width: | Height: | Size: 2.6 KiB |
101
changelog.md
101
changelog.md
@@ -1,9 +1,85 @@
|
||||
# Version History
|
||||
|
||||
|
||||
|
||||
All notable changes to the Zulip desktop app are documented in this file.
|
||||
|
||||
### v2.3.82 --2018-09-25
|
||||
|
||||
**New features**:
|
||||
* UI enhancements.
|
||||
* Updated some menu items.
|
||||
|
||||
**Fixes**:
|
||||
* Fix about page not opening up.
|
||||
|
||||
<hr>
|
||||
|
||||
### v2.3.8 --2018-09-25
|
||||
|
||||
**New features**:
|
||||
* Auto hide menubar on Windows/Linux. Add a setting option for the same.
|
||||
* Improve design of setting page.
|
||||
* Toggle app on clicking the tray icon (Linux).
|
||||
* Update sidebar realm name when it's changed in webapp.
|
||||
* left-sidebar: Add initial character of realm name instead of default icon.
|
||||
|
||||
**Fixes**:
|
||||
* linux: Fix ALT+SHIFT opening menu items on Linux.
|
||||
* sentry: Add ignore errors to sentry configuration.
|
||||
* Linux: Add label for help menu item.
|
||||
* file-attachments: Allow multiple downloads of same file name.
|
||||
|
||||
**Module Updates**
|
||||
* electron: Update electron to v2.0.9.
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
|
||||
### v2.3.7-beta --2018-09-03
|
||||
|
||||
**New features**:
|
||||
* Add a feature to show and view pdf file in the app.
|
||||
|
||||
**Fixes**:
|
||||
* Use package reload instead of custom reload. This is an experimental fix for setting files getting corrupted issue.
|
||||
* Unescape server name in window menu item.
|
||||
|
||||
<hr>
|
||||
|
||||
### v2.3.6 --2018-08-27
|
||||
|
||||
**New features**:
|
||||
* Add proxy details while validating a server. This fixes the server validating issue for users who are using the proxy settings.
|
||||
|
||||
|
||||
**Fixes**:
|
||||
|
||||
* Fix youtube video not playing in lightbox.
|
||||
* Fix realm name not escaped properly.
|
||||
|
||||
<hr>
|
||||
|
||||
### v2.3.5 --2018-08-03
|
||||
|
||||
**New features**:
|
||||
* Add a setting option to show downloaded file in file manager.
|
||||
* Added electron bridge to communicate with webapp in real time.
|
||||
|
||||
**Fixes**:
|
||||
|
||||
* Fix failing attached file downloads.
|
||||
* Fix page_params error.
|
||||
* gulpfile: Update syntax and methods for gulp v4.x.
|
||||
|
||||
<hr>
|
||||
|
||||
### v2.3.4-beta --2018-07-24
|
||||
|
||||
**Fixes**:
|
||||
* Fix downloading functionality of file attachments.
|
||||
* Fix null of downloadPath when settings.json fails.
|
||||
|
||||
<hr>
|
||||
|
||||
### v2.3.3 --2018-07-14
|
||||
|
||||
@@ -27,6 +103,7 @@ electron-builder: v20.20.4
|
||||
|
||||
electron-updater: v2.23.3
|
||||
|
||||
<hr>
|
||||
|
||||
### v2.3.2 --2018-05-28
|
||||
|
||||
@@ -215,7 +292,7 @@ electron-updater - `v2.21.8`
|
||||
|
||||
* Setting page and left-sidebar UI improvements
|
||||
|
||||
* Other minor improvments
|
||||
* Other minor improvements
|
||||
|
||||
|
||||
|
||||
@@ -342,7 +419,7 @@ electron-updater - `v2.21.8`
|
||||
|
||||
* Fixed a bug which was caused by app's shortcuts. From now on our shortcuts won't hijack other apps shortcuts
|
||||
|
||||
* Removed [electron-localshortcut](https://github.com/parro-it/electron-localshortcut) completely. Now we only depends on menu accelerators for keyboard shortcuts
|
||||
* Removed [electron-localshortcut](https://github.com/parro-it/electron-localshortcut) completely. Now we only depend on menu accelerators for keyboard shortcuts
|
||||
|
||||
* Handle certificate issue properly
|
||||
|
||||
@@ -543,7 +620,7 @@ electron-updater - `v2.21.8`
|
||||
|
||||
* Properly signed app for macOS
|
||||
* Toggle tray icon
|
||||
* Better error handling when no internet connenction
|
||||
* Better error handling when no internet connection
|
||||
|
||||
|
||||
|
||||
@@ -557,7 +634,7 @@ electron-updater - `v2.21.8`
|
||||
|
||||
### v0.5.8 --2017-02-13
|
||||
|
||||
**Enhacements**:
|
||||
**Enhancements**:
|
||||
Smaller Windows installer size
|
||||
|
||||
<hr>
|
||||
@@ -573,7 +650,7 @@ Minor improvements
|
||||
### v0.5.6 --2017-02-07
|
||||
|
||||
|
||||
**Enhacements**:
|
||||
**Enhancement**:
|
||||
|
||||
- Using NSIS instead of [Squirrel.Windows](https://github.com/Squirrel/Squirrel.Windows) on Windows
|
||||
|
||||
@@ -595,9 +672,9 @@ Minor improvements
|
||||
|
||||
**Fixes**:
|
||||
- Fixed :
|
||||
- Auto-updates
|
||||
- Spellchecker
|
||||
- Zooming functionality
|
||||
- Auto-updates
|
||||
- Spellchecker
|
||||
- Zooming functionality
|
||||
|
||||
- Removed unused node modules
|
||||
- Using stable version for node modules
|
||||
@@ -641,7 +718,7 @@ Minor improvements
|
||||
|
||||
### v0.5.1 --2016-11-23
|
||||
|
||||
**Enhacements**:
|
||||
**Enhancement**:
|
||||
|
||||
- Added Spellchecker support with correct spell suggestions
|
||||
|
||||
@@ -682,6 +759,6 @@ Minor improvements
|
||||
|
||||
### v0.0.1-alpha -- 2016-08-31
|
||||
|
||||
**Enhacements**:
|
||||
**Enhancement**:
|
||||
|
||||
* Added DMG installer for macOS
|
||||
|
||||
191
development.md
191
development.md
@@ -1,85 +1,152 @@
|
||||
# Improve development guide
|
||||
|
||||
# Development setup
|
||||
|
||||
This is a guide to running the Zulip desktop app from a source tree,
|
||||
in order to contribute to developing it.
|
||||
This is a guide to running the Zulip desktop app from a source tree, in order to contribute to developing it. The Zulip electron development environment can be installed on **macOS, Windows, and Linux** (Debian or Ubuntu recommended). You’ll need at least **2GB of available RAM**. Installing the Zulip electron development environment requires downloading several hundred megabytes of dependencies, so you will need an **active, reasonably fast, internet connection throughout the entire installation processes.**
|
||||
|
||||
## Prerequisites
|
||||
# Set up Git & GitHub
|
||||
|
||||
To build and run the app from source, you'll need the following:
|
||||
You can skip this step if you already have Git, GitHub.
|
||||
|
||||
* [Git](http://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
|
||||
* [Node.js](https://nodejs.org) >= v6.9.0
|
||||
* [NPM](https://www.npmjs.com/get-npm) and
|
||||
[node-gyp](https://github.com/nodejs/node-gyp#installation),
|
||||
if they don't come bundled with your Node.js installation
|
||||
* [Python](https://www.python.org/downloads/release/python-2713/)
|
||||
(v2.7.x recommended)
|
||||
* A C++ compiler compatible with C++11
|
||||
* Linux users also need [Snapcraft](https://snapcraft.io/)
|
||||
* Development headers for the libXext, libXtst, and libxkbfile libraries
|
||||
Follow our [Git Guide](https://zulip.readthedocs.io/en/latest/git/setup.html) in order to install Git, set up a GitHub account
|
||||
|
||||
### Debian/Ubuntu and friends
|
||||
|
||||
On a system running Debian, Ubuntu, or another Debian-based Linux
|
||||
distribution, you can install all dependencies through the package
|
||||
manager (see [here][nodesource-install] for more on the first command):
|
||||
# Install Prerequisites
|
||||
|
||||
```sh
|
||||
$ curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
|
||||
$ sudo apt install git nodejs python build-essential snapcraft libxext-dev libxtst-dev libxkbfile-dev libgconf-2-4
|
||||
```
|
||||
Jump to:
|
||||
|
||||
[nodesource-install]: https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions
|
||||
- [MacOS](https://github.com/zulip/zulip-desktop/blob/master/development.md#macos)
|
||||
- [Ubuntu/Debian](https://github.com/zulip/zulip-desktop/blob/master/development.md#ubuntudebian)
|
||||
- [Windows](https://github.com/zulip/zulip-desktop/blob/master/development.md#windows)
|
||||
|
||||
### Other OSes
|
||||
|
||||
Other developers run the app on Windows, macOS, and possibly other OSes.
|
||||
PRs to add specific instructions to this doc are welcome!
|
||||
## MacOS
|
||||
|
||||
On Windows, your C++ compiler should be Visual Studio 2015 or later.
|
||||
|
||||
## Download, build, and run
|
||||
**Node JS**
|
||||
Go to the [Node.js Downloads page](https://nodejs.org/en/download/). Download Node.js for MacOS (`v6.9.0` or above recommended). Run the downloaded Node.js `.pkg` Installer. You're finished! To ensure Node.js has been installed, run `node -v` in your terminal - you should get something like `v6.9.0` or above
|
||||
|
||||
Clone the source locally:
|
||||
```sh
|
||||
$ git clone https://github.com/zulip/zulip-electron
|
||||
$ cd zulip-electron
|
||||
```
|
||||
|
||||
Install project dependencies:
|
||||
```sh
|
||||
$ npm install
|
||||
```
|
||||
**if** [**NPM**](https://www.npmjs.com/get-npm) **and** [**node-gyp**](https://github.com/nodejs/node-gyp#installation) **don't come bundled with your Node.js installation, Download manually**
|
||||
|
||||
Start the app:
|
||||
```sh
|
||||
$ npm start
|
||||
```
|
||||
|
||||
Start and watch changes:
|
||||
```sh
|
||||
$ npm run dev
|
||||
```
|
||||
Now you are ready for next step [: Get Zulip Desktop Code.](https://github.com/zulip/zulip-desktop/blob/master/development.md#get-zulip-desktop-code)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you have any problems running the app, see the [most common
|
||||
issues](./troubleshooting.md).
|
||||
## Ubuntu/Debian
|
||||
|
||||
## Making a release
|
||||
|
||||
To package the app into an installer:
|
||||
```
|
||||
npm run dist
|
||||
```
|
||||
If you’re in a hurry, you can copy and paste the following into your terminal
|
||||
|
||||
This command will produce distributable packages or installers for the
|
||||
operating system you're running on:
|
||||
* on Windows, a Windows installer file
|
||||
* on macOS, a `.dmg` file
|
||||
* on Linux, a plain `.zip` file as well as a `.deb` file, `.snap` file and an
|
||||
`AppImage` file.
|
||||
To generate all three types, you will need all three operating
|
||||
systems.
|
||||
sudo apt install git nodejs node-gyp python build-essential snapcraft libxext-dev libxtst-dev lib xkbfile-dev libgconf-2-4
|
||||
|
||||
The output files appear in the `dist/` directory.
|
||||
after pasting you can jump to next step [: Get Zulip Desktop Code](https://github.com/zulip/zulip-desktop/blob/master/development.md#get-zulip-desktop-code).
|
||||
|
||||
|
||||
**For a step-by-step explanation, read on.**
|
||||
|
||||
1. **Node JS**
|
||||
|
||||
`$ sudo apt-get install nodejs`
|
||||
|
||||
2. **Install** [**Node-gyp**](https://github.com/nodejs/node-gyp#installation)
|
||||
|
||||
3. **Python (v2.7.x recommended)**
|
||||
|
||||
`$ sudo apt install python2.7`
|
||||
|
||||
4. **C++ compiler compatible with C++11**
|
||||
|
||||
`$ sudo apt install build-essential`
|
||||
|
||||
5. **Snapcraft**
|
||||
|
||||
`$ sudo apt install snapcraft`
|
||||
|
||||
6. **Development** **headers**
|
||||
|
||||
`$ sudo apt install libxext-dev libxtst-dev libxkbfile-dev libgconf-2-4`
|
||||
|
||||
|
||||
**if** [**NPM**](https://www.npmjs.com/get-npm) **don't come bundled with your Node.js installation, Download manually**
|
||||
|
||||
|
||||
Now you are ready for next step [: Get Zulip Desktop Code.](https://github.com/zulip/zulip-desktop/blob/master/development.md#get-zulip-desktop-code)
|
||||
|
||||
|
||||
## Windows
|
||||
|
||||
**Node JS**
|
||||
Go to the [Node.js Downloads page](https://nodejs.org/en/download/). Download Node.js for windows (`v6.9.0` or above recommended). Run the downloaded Node.js `.msi` Installer. You're finished! To ensure Node.js has been installed, run `node -v` in your terminal - you should get something like `v6.9.0` or above
|
||||
|
||||
|
||||
**Followings are optional yet recommended prerequisites -**
|
||||
|
||||
**Cmder**
|
||||
1. Download the [latest release](https://github.com/cmderdev/cmder/releases/)
|
||||
2. Extract the archive. *Note: This path should not be* `C:\Program Files` *or anywhere else that would require Administrator access for modifying configuration files*
|
||||
3. (optional) Place your own executable files into the `%cmder_root%\bin` folder to be injected into your PATH.
|
||||
4. Run `Cmder.exe`
|
||||
|
||||
**Chocolatey**
|
||||
You can download chocolatey from here https://chocolatey.org/ and for Installing Chocolatey on your machine follow this steps
|
||||
1. First, ensure that you are using an administrative shell.
|
||||
2. Copy the text specific to your command shell - [cmd.exe](https://chocolatey.org/install#install-with-cmdexe) or [powershell.exe](https://chocolatey.org/install#install-with-powershellexe).
|
||||
3. Paste the copied text into your shell and press Enter.
|
||||
4. Wait a few seconds for the command to complete.
|
||||
5. If you don't see any errors, you are ready to use Chocolatey! Type `choco` or `choco -?`
|
||||
|
||||
|
||||
**System specific dependencies**
|
||||
|
||||
- use only 32bit or 64bit for all of the installers, do not mix architectures
|
||||
- install using default settings
|
||||
- open Windows Powershell as Admin and paste this
|
||||
C:\Windows\system32> npm install --global --production windows-build-tools
|
||||
|
||||
|
||||
**if** [**NPM**](https://www.npmjs.com/get-npm) **and** [**node-gyp**](https://github.com/nodejs/node-gyp#installation) **don't come bundled with your Node.js installation, Download manually**
|
||||
|
||||
Now you are ready for next step [: Get Zulip Desktop Code.](https://github.com/zulip/zulip-desktop/blob/master/development.md#get-zulip-desktop-code)
|
||||
|
||||
|
||||
# Get Zulip Desktop Code
|
||||
|
||||
1. In your browser, visit https://github.com/zulip/zulip-desktop and click the `fork` button. You will need to be logged in to GitHub to do this.
|
||||
2. Open Terminal (macOS/Ubuntu) or Git BASH (Windows; must **run as an Administrator**).
|
||||
3. In Terminal/Git BASH, [clone your fork of the zulip-desktop repository](https://github.com/zulip/zulip-desktop/blob/master/development.md#clone-to-your-machine) and [connect the zulip-desktop upstream repository](https://github.com/zulip/zulip-desktop/blob/master/development.md#connect-your-fork-to-zulip-desktop-upstream)
|
||||
|
||||
|
||||
## Clone to your machine
|
||||
1. On GitHub, navigate to the main page of your fork repository.
|
||||
2. Under the repository name, click **Clone or download**.
|
||||
3. In the Clone with HTTPs section, click to copy the clone URL for the repository.
|
||||
4. Open Terminal, Change the current working directory to the location where you want the cloned directory to be made.
|
||||
|
||||
git clone https://github.com/YOURUSERNAME/zulip-desktop.git
|
||||
|
||||
Don’t forget to replace YOURUSERNAME with your git username
|
||||
|
||||
|
||||
## Connect your fork to zulip-desktop upstream
|
||||
|
||||
cd zulip-desktop
|
||||
git remote add -f upstream https://github.com/zulip/zulip-desktop.git
|
||||
|
||||
|
||||
# build and run
|
||||
|
||||
|
||||
## Install project dependencies:
|
||||
$ npm install
|
||||
|
||||
|
||||
## There two ways to start the app:
|
||||
|
||||
**vanilla method**
|
||||
|
||||
$ npm start
|
||||
|
||||
**start and watch changes recommended for dev’s**
|
||||
|
||||
$ npm run dev
|
||||
|
||||
@@ -21,8 +21,8 @@ C:\Windows\system32> npm install --global --production windows-build-tools
|
||||
Clone the source locally:
|
||||
|
||||
```sh
|
||||
$ git clone https://github.com/zulip/zulip-electron
|
||||
$ cd zulip-electron
|
||||
$ git clone https://github.com/zulip/zulip-desktop
|
||||
$ cd zulip-desktop
|
||||
```
|
||||
|
||||
Install project dependencies:
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
### Want to contribute to this Wiki?
|
||||
|
||||
[Edit `/docs` files and send a pull request.](https://github.com/zulip/zulip-electron/tree/master/docs)
|
||||
[Edit `/docs` files and send a pull request.](https://github.com/zulip/zulip-desktop/tree/master/docs)
|
||||
|
||||
10
gulpfile.js
10
gulpfile.js
@@ -10,11 +10,11 @@ gulp.task('dev', () => {
|
||||
// Start browser process
|
||||
electron.start();
|
||||
// Restart browser process
|
||||
gulp.watch('app/main/*.js', ['restart:browser']);
|
||||
gulp.watch('app/main/*.js', gulp.series('restart:browser'));
|
||||
// Reload renderer process
|
||||
gulp.watch('app/renderer/css/*.css', ['reload:renderer']);
|
||||
gulp.watch('app/renderer/*.html', ['reload:renderer']);
|
||||
gulp.watch('app/renderer/js/**/*.js', ['reload:renderer']);
|
||||
gulp.watch('app/renderer/css/*.css', gulp.series('reload:renderer'));
|
||||
gulp.watch('app/renderer/*.html', gulp.series('reload:renderer'));
|
||||
gulp.watch('app/renderer/js/**/*.js', gulp.series('reload:renderer'));
|
||||
});
|
||||
|
||||
gulp.task('restart:browser', done => {
|
||||
@@ -36,4 +36,4 @@ gulp.task('test-e2e', () => {
|
||||
}));
|
||||
});
|
||||
|
||||
gulp.task('default', ['dev', 'test-e2e']);
|
||||
gulp.task('default', gulp.parallel('dev', 'test-e2e'));
|
||||
|
||||
105
help.md
105
help.md
@@ -1,105 +0,0 @@
|
||||
# User Guide
|
||||
|
||||
> Welcome! This guide will walk you through the basics of using Zulip Desktop.
|
||||
|
||||
|
||||
## Get Zulip Desktop
|
||||
|
||||
|
||||
|
||||
## Connect to a Server
|
||||
|
||||
### Connect through a proxy
|
||||
|
||||
It's possible to connect to your server through a proxy.
|
||||
You can enter the proxy settings in the `Network` section of App Settings.
|
||||
There are three fields provided:
|
||||
* `PAC script` - The URL associated with the PAC file.
|
||||
* `Proxy rules` - Rules indicating which proxies to use.
|
||||
* `Proxy bypass rules` - Rules indicating which URLs should
|
||||
bypass the proxy settings.
|
||||
|
||||
For a typical setup where internet access is required to use an HTTP proxy,
|
||||
but URLs on the local network should be accessed directly, configure as follows:
|
||||
|
||||
`Proxy rules = proxy.example.com`
|
||||
|
||||
Your HTTP proxy server
|
||||
`Proxy bypass rules = *.example.com;10.0.0.0/8`
|
||||
|
||||
Directly connect to your own domain and private IP subnet
|
||||
for more complex setups, read below to configure complex proxy rules and proxy bypass rules.
|
||||
|
||||
### Sets the proxy settings.
|
||||
|
||||
When `PAC script` and `Proxy rules` are provided together, the `Proxy rules`
|
||||
option is ignored and `PAC script` configuration is applied.
|
||||
|
||||
The `Proxy rules` has to follow the rules below:
|
||||
|
||||
```
|
||||
proxyRules = schemeProxies[";"<schemeProxies>]
|
||||
schemeProxies = [<urlScheme>"="]<proxyURIList>
|
||||
urlScheme = "http" | "https" | "ftp" | "socks"
|
||||
proxyURIList = <proxyURL>[","<proxyURIList>]
|
||||
proxyURL = [<proxyScheme>"://"]<proxyHost>[":"<proxyPort>]
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
* `http=foopy:80;ftp=foopy2` - Use HTTP proxy `foopy:80` for `http://` URLs, and
|
||||
HTTP proxy `foopy2:80` for `ftp://` URLs.
|
||||
* `foopy:80` - Use HTTP proxy `foopy:80` for all URLs.
|
||||
* `foopy:80,bar,direct://` - Use HTTP proxy `foopy:80` for all URLs, failing
|
||||
over to `bar` if `foopy:80` is unavailable, and after that using no proxy.
|
||||
* `socks4://foopy` - Use SOCKS v4 proxy `foopy:1080` for all URLs.
|
||||
* `http=foopy,socks5://bar.com` - Use HTTP proxy `foopy` for http URLs, and fail
|
||||
over to the SOCKS5 proxy `bar.com` if `foopy` is unavailable.
|
||||
* `http=foopy,direct://` - Use HTTP proxy `foopy` for http URLs, and use no
|
||||
proxy if `foopy` is unavailable.
|
||||
* `http=foopy;socks=foopy2` - Use HTTP proxy `foopy` for http URLs, and use
|
||||
`socks4://foopy2` for all other URLs.
|
||||
|
||||
The `Proxy bypass rules` is a comma separated list of rules described below:
|
||||
|
||||
* `[ URL_SCHEME "://" ] HOSTNAME_PATTERN [ ":" <port> ]`
|
||||
|
||||
Match all hostnames that match the pattern HOSTNAME_PATTERN.
|
||||
|
||||
Examples:
|
||||
"foobar.com", "*foobar.com", "*.foobar.com", "*foobar.com:99",
|
||||
"https://x.*.y.com:99"
|
||||
|
||||
* `"." HOSTNAME_SUFFIX_PATTERN [ ":" PORT ]`
|
||||
|
||||
Match a particular domain suffix.
|
||||
|
||||
Examples:
|
||||
".google.com", ".com", "http://.google.com"
|
||||
|
||||
* `[ SCHEME "://" ] IP_LITERAL [ ":" PORT ]`
|
||||
|
||||
Match URLs which are IP address literals.
|
||||
|
||||
Examples:
|
||||
"127.0.1", "[0:0::1]", "[::1]", "http://[::1]:99"
|
||||
|
||||
* `IP_LITERAL "/" PREFIX_LENGHT_IN_BITS`
|
||||
|
||||
Match any URL that is to an IP literal that falls between the
|
||||
given range. IP range is specified using CIDR notation.
|
||||
|
||||
Examples:
|
||||
"192.168.1.1/16", "fefe:13::abc/33".
|
||||
|
||||
* `<local>`
|
||||
|
||||
Match local addresses. The meaning of `<local>` is whether the
|
||||
host matches one of: "127.0.0.1", "::1", "localhost".
|
||||
|
||||
|
||||
## Change App Preferences
|
||||
|
||||
|
||||
|
||||
## Reporting an Issue
|
||||
@@ -1,10 +1,10 @@
|
||||
# How to install
|
||||
|
||||
**Note:** If you download from the [releases page](https://github.com/zulip/zulip-electron/releases), be careful what version you pick. Releases that end with `-beta` are beta releases and the rest are stable.
|
||||
**Note:** If you download from the [releases page](https://github.com/zulip/zulip-desktop/releases), be careful what version you pick. Releases that end with `-beta` are beta releases and the rest are stable.
|
||||
- **beta:** these releases are the right balance between getting new features early while staying away from nasty bugs.
|
||||
- **stable:** these releases are more thoroughly tested; they receive new features later, but there's a lower chance that things will go wrong.
|
||||
|
||||
[LR]: https://github.com/zulip/zulip-electron/releases
|
||||
[LR]: https://github.com/zulip/zulip-desktop/releases
|
||||
|
||||
## OS X
|
||||
|
||||
|
||||
3882
package-lock.json
generated
3882
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
42
package.json
42
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "zulip",
|
||||
"productName": "Zulip",
|
||||
"version": "2.3.4-beta",
|
||||
"version": "3.0.0",
|
||||
"main": "./app/main",
|
||||
"description": "Zulip Desktop App",
|
||||
"license": "Apache-2.0",
|
||||
@@ -12,10 +12,10 @@
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zulip/zulip-electron.git"
|
||||
"url": "https://github.com/zulip/zulip-desktop.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/zulip/zulip-electron/issues"
|
||||
"url": "https://github.com/zulip/zulip-desktop/issues"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
@@ -24,7 +24,10 @@
|
||||
"start": "electron app --disable-http-cache --no-electron-connect",
|
||||
"reinstall": "node ./tools/reinstall-node-modules.js",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"test": "xo",
|
||||
"lint-css": "stylelint app/renderer/css/*.css",
|
||||
"lint-html": "./node_modules/.bin/htmlhint \"app/renderer/*.html\" ",
|
||||
"lint-js": "xo",
|
||||
"test": "npm run lint-html && npm run lint-css && npm run lint-js",
|
||||
"test-e2e": "gulp test-e2e",
|
||||
"dev": "gulp dev & nodemon --watch app/main --watch app/renderer --exec 'npm test' -e html,css,js",
|
||||
"pack": "electron-builder --dir",
|
||||
@@ -44,7 +47,7 @@
|
||||
"!docs${/*}",
|
||||
"!node_modules/@paulcbetts/cld/deps/cld${/*}"
|
||||
],
|
||||
"copyright": "©2017 Kandra Labs, Inc.",
|
||||
"copyright": "©2019 Kandra Labs, Inc.",
|
||||
"mac": {
|
||||
"category": "public.app-category.productivity",
|
||||
"artifactName": "${productName}-${version}-${arch}.${ext}"
|
||||
@@ -59,7 +62,7 @@
|
||||
"AppImage",
|
||||
"snap"
|
||||
],
|
||||
"maintainer": "Akash Nimare <svnitakash@gmail.com>",
|
||||
"maintainer": "Akash Nimare <akash@zulipchat.com>",
|
||||
"artifactName": "${productName}-${version}-${arch}.${ext}"
|
||||
},
|
||||
"deb": {
|
||||
@@ -103,12 +106,13 @@
|
||||
}
|
||||
],
|
||||
"icon": "build/icon.ico",
|
||||
"artifactName": "${productName}-Web-Setup-${version}.${ext}",
|
||||
"publisherName": "Kandra Labs, Inc."
|
||||
},
|
||||
"nsis": {
|
||||
"perMachine": true,
|
||||
"allowToChangeInstallationDirectory": true,
|
||||
"oneClick": false,
|
||||
"allowToChangeInstallationDirectory": true
|
||||
"perMachine": false
|
||||
}
|
||||
},
|
||||
"keywords": [
|
||||
@@ -121,21 +125,23 @@
|
||||
],
|
||||
"devDependencies": {
|
||||
"assert": "1.4.1",
|
||||
"cp-file": "^5.0.0",
|
||||
"cp-file": "5.0.0",
|
||||
"devtron": "1.4.0",
|
||||
"electron": "2.0.1",
|
||||
"electron-builder": "20.20.4",
|
||||
"electron": "3.0.10",
|
||||
"electron-builder": "20.40.2",
|
||||
"electron-connect": "0.6.2",
|
||||
"electron-debug": "1.4.0",
|
||||
"google-translate-api": "2.3.0",
|
||||
"gulp": "^4.0.0",
|
||||
"gulp": "4.0.0",
|
||||
"gulp-tape": "0.0.9",
|
||||
"is-ci": "^1.0.10",
|
||||
"nodemon": "^1.14.11",
|
||||
"htmlhint": "0.11.0",
|
||||
"is-ci": "1.0.10",
|
||||
"nodemon": "1.14.11",
|
||||
"pre-commit": "1.2.2",
|
||||
"spectron": "3.8.0",
|
||||
"tap-colorize": "^1.2.0",
|
||||
"tape": "^4.8.0",
|
||||
"spectron": "5.0.0",
|
||||
"stylelint": "9.10.1",
|
||||
"tap-colorize": "1.2.0",
|
||||
"tape": "4.8.0",
|
||||
"xo": "0.18.2"
|
||||
},
|
||||
"xo": {
|
||||
@@ -153,7 +159,7 @@
|
||||
"max-lines": [
|
||||
"warn",
|
||||
{
|
||||
"max": 500,
|
||||
"max": 700,
|
||||
"skipBlankLines": true,
|
||||
"skipComments": true
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: zulip
|
||||
version: 2.0.0
|
||||
version: 2.3.82
|
||||
summary: Zulip Desktop Client for Linux
|
||||
description: Zulip combines the immediacy of Slack with an email threading model. With Zulip, you can catch up on important conversations while ignoring irrelevant ones.
|
||||
confinement: strict
|
||||
@@ -34,4 +34,4 @@ parts:
|
||||
- libxtst6
|
||||
source: dist/linux-unpacked
|
||||
after:
|
||||
- desktop-gtk2
|
||||
- desktop-gtk3
|
||||
|
||||
183
tests/e2e/package.json
Normal file
183
tests/e2e/package.json
Normal file
@@ -0,0 +1,183 @@
|
||||
{
|
||||
"name": "zulip",
|
||||
"productName": "Zulip",
|
||||
"version": "2.4.0",
|
||||
"main": "../../app/main",
|
||||
"description": "Zulip Desktop App",
|
||||
"license": "Apache-2.0",
|
||||
"copyright": "Kandra Labs, Inc.",
|
||||
"author": {
|
||||
"name": "Kandra Labs, Inc.",
|
||||
"email": "support@zulipchat.com"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zulip/zulip-electron.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/zulip/zulip-electron/issues"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "electron app --disable-http-cache --no-electron-connect",
|
||||
"reinstall": "node ./tools/reinstall-node-modules.js",
|
||||
"postinstall": "electron-builder install-app-deps",
|
||||
"test": "xo",
|
||||
"test-e2e": "gulp test-e2e",
|
||||
"dev": "gulp dev & nodemon --watch app/main --watch app/renderer --exec 'npm test' -e html,css,js",
|
||||
"pack": "electron-builder --dir",
|
||||
"dist": "electron-builder",
|
||||
"mas": "electron-builder --mac mas",
|
||||
"travis": "cd ./scripts && ./travis-build-test.sh",
|
||||
"build-locales": "node tools/locale-helper"
|
||||
},
|
||||
"pre-commit": [
|
||||
"test"
|
||||
],
|
||||
"build": {
|
||||
"appId": "org.zulip.zulip-electron",
|
||||
"asar": true,
|
||||
"files": [
|
||||
"**/*",
|
||||
"!docs${/*}",
|
||||
"!node_modules/@paulcbetts/cld/deps/cld${/*}"
|
||||
],
|
||||
"copyright": "©2017 Kandra Labs, Inc.",
|
||||
"mac": {
|
||||
"category": "public.app-category.productivity",
|
||||
"artifactName": "${productName}-${version}-${arch}.${ext}"
|
||||
},
|
||||
"linux": {
|
||||
"category": "Chat;GNOME;GTK;Network;InstantMessaging",
|
||||
"packageCategory": "GNOME;GTK;Network;InstantMessaging",
|
||||
"description": "Zulip Desktop Client for Linux",
|
||||
"target": [
|
||||
"deb",
|
||||
"zip",
|
||||
"AppImage",
|
||||
"snap"
|
||||
],
|
||||
"maintainer": "Akash Nimare <svnitakash@gmail.com>",
|
||||
"artifactName": "${productName}-${version}-${arch}.${ext}"
|
||||
},
|
||||
"deb": {
|
||||
"synopsis": "Zulip Desktop App",
|
||||
"afterInstall": "./scripts/debian-add-repo.sh",
|
||||
"afterRemove": "./scripts/debian-uninstaller.sh"
|
||||
},
|
||||
"snap": {
|
||||
"synopsis": "Zulip Desktop App"
|
||||
},
|
||||
"dmg": {
|
||||
"background": "build/appdmg.png",
|
||||
"icon": "build/icon.icns",
|
||||
"iconSize": 100,
|
||||
"contents": [
|
||||
{
|
||||
"x": 380,
|
||||
"y": 280,
|
||||
"type": "link",
|
||||
"path": "/Applications"
|
||||
},
|
||||
{
|
||||
"x": 110,
|
||||
"y": 280,
|
||||
"type": "file"
|
||||
}
|
||||
],
|
||||
"window": {
|
||||
"width": 500,
|
||||
"height": 500
|
||||
}
|
||||
},
|
||||
"win": {
|
||||
"target": [
|
||||
{
|
||||
"target": "nsis-web",
|
||||
"arch": [
|
||||
"x64",
|
||||
"ia32"
|
||||
]
|
||||
}
|
||||
],
|
||||
"icon": "build/icon.ico",
|
||||
"publisherName": "Kandra Labs, Inc."
|
||||
},
|
||||
"nsis": {
|
||||
"allowToChangeInstallationDirectory": true
|
||||
}
|
||||
},
|
||||
"keywords": [
|
||||
"Zulip",
|
||||
"Group Chat app",
|
||||
"electron-app",
|
||||
"electron",
|
||||
"Desktop app",
|
||||
"InstantMessaging"
|
||||
],
|
||||
"devDependencies": {
|
||||
"assert": "1.4.1",
|
||||
"cp-file": "^5.0.0",
|
||||
"devtron": "1.4.0",
|
||||
"electron": "3.0.10",
|
||||
"electron-builder": "20.38.4",
|
||||
"electron-connect": "0.6.2",
|
||||
"electron-debug": "1.4.0",
|
||||
"google-translate-api": "2.3.0",
|
||||
"gulp": "^4.0.0",
|
||||
"gulp-tape": "0.0.9",
|
||||
"is-ci": "^1.0.10",
|
||||
"nodemon": "^1.14.11",
|
||||
"pre-commit": "1.2.2",
|
||||
"spectron": "3.8.0",
|
||||
"tap-colorize": "^1.2.0",
|
||||
"tape": "^4.8.0",
|
||||
"xo": "0.18.2"
|
||||
},
|
||||
"xo": {
|
||||
"parserOptions": {
|
||||
"sourceType": "script",
|
||||
"ecmaFeatures": {
|
||||
"globalReturn": true
|
||||
}
|
||||
},
|
||||
"esnext": true,
|
||||
"overrides": [
|
||||
{
|
||||
"files": "app*/**/*.js",
|
||||
"rules": {
|
||||
"max-lines": [
|
||||
"warn",
|
||||
{
|
||||
"max": 600,
|
||||
"skipBlankLines": true,
|
||||
"skipComments": true
|
||||
}
|
||||
],
|
||||
"no-warning-comments": 0,
|
||||
"object-curly-spacing": 0,
|
||||
"capitalized-comments": 0,
|
||||
"no-else-return": 0,
|
||||
"no-path-concat": 0,
|
||||
"no-alert": 0,
|
||||
"guard-for-in": 0,
|
||||
"prefer-promise-reject-errors": 0,
|
||||
"import/no-unresolved": 0,
|
||||
"import/no-extraneous-dependencies": 0,
|
||||
"no-prototype-builtins": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"ignore": [
|
||||
"tests/*.js",
|
||||
"tools/locale-helper/*.js"
|
||||
],
|
||||
"envs": [
|
||||
"node",
|
||||
"browser",
|
||||
"mocha"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -48,10 +48,10 @@ envSetup()
|
||||
|
||||
# Set name of upstreamRemote
|
||||
cd $workingDir
|
||||
git remote -v | grep "github\.com.zulip.zulip-electron.git (fetch)" > /dev/null 2>&1
|
||||
git remote -v | grep "github\.com.zulip.zulip-desktop.git (fetch)" > /dev/null 2>&1
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
upstreamRemote=`git remote -v | grep "github\.com.zulip.zulip-electron.git (fetch)" | awk '{ print $1 }'`
|
||||
upstreamRemote=`git remote -v | grep "github\.com.zulip.zulip-desktop.git (fetch)" | awk '{ print $1 }'`
|
||||
else
|
||||
upstreamRemote="origin"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user