mirror of
https://github.com/zulip/zulip-desktop.git
synced 2025-11-06 15:03:20 +00:00
Compare commits
35 Commits
setup-karm
...
v1.8.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8d7a79877 | ||
|
|
6e6db42b54 | ||
|
|
db79284fbb | ||
|
|
2434f06655 | ||
|
|
1d611d3382 | ||
|
|
a746194e9e | ||
|
|
7cc13f7a26 | ||
|
|
6a9bb152a0 | ||
|
|
8b6dcd355f | ||
|
|
91742a5770 | ||
|
|
fb74251a2c | ||
|
|
a920720f91 | ||
|
|
aa8e99b7a6 | ||
|
|
e23f8aaa58 | ||
|
|
5c3208d44c | ||
|
|
c0b57bbe2b | ||
|
|
afe4e8901b | ||
|
|
231e7fd9c2 | ||
|
|
a0d898a5b7 | ||
|
|
1abf62555c | ||
|
|
6befcbaa8f | ||
|
|
e56a01049b | ||
|
|
72cb8459ff | ||
|
|
0b83b22206 | ||
|
|
267d25e5c4 | ||
|
|
8401f8f5ce | ||
|
|
c4a7264f34 | ||
|
|
9d081ecd5a | ||
|
|
dc6582fa82 | ||
|
|
3b412672c6 | ||
|
|
04083bfa81 | ||
|
|
562e82d2f1 | ||
|
|
3b014e0715 | ||
|
|
13178ebc8f | ||
|
|
08693bf105 |
8
.github/ISSUE_TEMPLATE.md
vendored
Normal file
8
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
<!-- Please Include: -->
|
||||||
|
- **Operating System**:
|
||||||
|
- [ ] Windows
|
||||||
|
- [ ] Linux/Ubutnu
|
||||||
|
- [ ] macOS
|
||||||
|
- **Clear steps to reproduce the issue**:
|
||||||
|
- **Relevant error messages and/or screenshots**:
|
||||||
16
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
16
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
<!--
|
||||||
|
Remove the fields that are not appropriate
|
||||||
|
Please include:
|
||||||
|
-->
|
||||||
|
|
||||||
|
**What's this PR do?**
|
||||||
|
|
||||||
|
**Any background context you want to provide?**
|
||||||
|
|
||||||
|
**Screenshots?**
|
||||||
|
|
||||||
|
**You have tested this PR on:**
|
||||||
|
- [ ] Windows
|
||||||
|
- [ ] Linux/Ubuntu
|
||||||
|
- [ ] macOS
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -26,7 +26,6 @@ yarn-error.log*
|
|||||||
config.gypi
|
config.gypi
|
||||||
|
|
||||||
# Test generated files
|
# Test generated files
|
||||||
tests/e2e/package.json
|
tests/package.json
|
||||||
coverage
|
|
||||||
|
|
||||||
.python-version
|
.python-version
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
Please include:
|
|
||||||
- `Operating System`
|
|
||||||
- `Clear steps to reproduce the issue`
|
|
||||||
- `Relevant error messages and/or screenshots`
|
|
||||||
|
|
||||||
|
|
||||||
13
README.md
13
README.md
@@ -12,19 +12,12 @@ Please see [installation guide](https://zulipchat.com/help/desktop-app-install-g
|
|||||||
|
|
||||||
# Features
|
# Features
|
||||||
* Sign in to multiple teams
|
* Sign in to multiple teams
|
||||||
* Native desktop Notifications
|
* Desktop Notifications with inline reply support
|
||||||
* SpellChecker
|
* Multilanguage SpellChecker
|
||||||
* OSX/Win/Linux installers
|
* OSX/Win/Linux installers
|
||||||
* Automatic Updates (macOS/Windows)
|
* Automatic Updates (macOS/Windows/Linux)
|
||||||
* Keyboard shortcuts
|
* Keyboard shortcuts
|
||||||
|
|
||||||
Description | Keys
|
|
||||||
-----------------------| -----------------------
|
|
||||||
Default shortcuts | <kbd>Cmd/Ctrl</kbd> <kbd>k</kbd>
|
|
||||||
Manage Zulip Servers | <kbd>Cmd/Ctrl</kbd> <kbd>,</kbd>
|
|
||||||
Back | <kbd>Cmd/Ctrl</kbd> <kbd>[</kbd>
|
|
||||||
Forward | <kbd>Cmd/Ctrl</kbd> <kbd>]</kbd>
|
|
||||||
|
|
||||||
# Development
|
# Development
|
||||||
Please see our [development guide](./development.md) to get started and run app locally.
|
Please see our [development guide](./development.md) to get started and run app locally.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
const fs = require('fs');
|
|
||||||
const { app, dialog } = require('electron');
|
const { app, dialog } = require('electron');
|
||||||
const { autoUpdater } = require('electron-updater');
|
const { autoUpdater } = require('electron-updater');
|
||||||
const isDev = require('electron-is-dev');
|
const isDev = require('electron-is-dev');
|
||||||
@@ -15,10 +14,6 @@ function appUpdater() {
|
|||||||
// Create Logs directory
|
// Create Logs directory
|
||||||
const LogsDir = `${app.getPath('userData')}/Logs`;
|
const LogsDir = `${app.getPath('userData')}/Logs`;
|
||||||
|
|
||||||
if (!fs.existsSync(LogsDir)) {
|
|
||||||
fs.mkdirSync(LogsDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log whats happening
|
// Log whats happening
|
||||||
const log = require('electron-log');
|
const log = require('electron-log');
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const { setAutoLaunch } = require('./startup');
|
|||||||
const { app, ipcMain } = electron;
|
const { app, ipcMain } = electron;
|
||||||
|
|
||||||
const BadgeSettings = require('./../renderer/js/pages/preference/badge-settings.js');
|
const BadgeSettings = require('./../renderer/js/pages/preference/badge-settings.js');
|
||||||
|
const ConfigUtil = require('./../renderer/js/utils/config-util.js');
|
||||||
|
|
||||||
// Adds debug features like hotkeys for triggering dev tools and reload
|
// Adds debug features like hotkeys for triggering dev tools and reload
|
||||||
// in development mode
|
// in development mode
|
||||||
@@ -81,7 +82,11 @@ function createMainWindow() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
win.once('ready-to-show', () => {
|
win.once('ready-to-show', () => {
|
||||||
win.show();
|
if (ConfigUtil.getConfigItem('startMinimized')) {
|
||||||
|
win.minimize();
|
||||||
|
} else {
|
||||||
|
win.show();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
win.loadURL(mainURL);
|
win.loadURL(mainURL);
|
||||||
@@ -124,6 +129,9 @@ function createMainWindow() {
|
|||||||
return win;
|
return win;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decrease load on GPU (experimental)
|
||||||
|
app.disableHardwareAcceleration();
|
||||||
|
|
||||||
// eslint-disable-next-line max-params
|
// eslint-disable-next-line max-params
|
||||||
app.on('certificate-error', (event, webContents, url, error, certificate, callback) => {
|
app.on('certificate-error', (event, webContents, url, error, certificate, callback) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@@ -145,7 +153,11 @@ app.on('ready', () => {
|
|||||||
const page = mainWindow.webContents;
|
const page = mainWindow.webContents;
|
||||||
|
|
||||||
page.on('dom-ready', () => {
|
page.on('dom-ready', () => {
|
||||||
mainWindow.show();
|
if (ConfigUtil.getConfigItem('startMinimized')) {
|
||||||
|
mainWindow.minimize();
|
||||||
|
} else {
|
||||||
|
mainWindow.show();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
page.once('did-frame-finish-load', () => {
|
page.once('did-frame-finish-load', () => {
|
||||||
@@ -155,7 +167,8 @@ app.on('ready', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
electron.powerMonitor.on('resume', () => {
|
electron.powerMonitor.on('resume', () => {
|
||||||
page.send('reload-viewer');
|
mainWindow.reload();
|
||||||
|
page.send('destroytray');
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMain.on('focus-app', () => {
|
ipcMain.on('focus-app', () => {
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ class AppMenu {
|
|||||||
role: 'togglefullscreen'
|
role: 'togglefullscreen'
|
||||||
}, {
|
}, {
|
||||||
label: 'Zoom In',
|
label: 'Zoom In',
|
||||||
accelerator: 'CommandOrControl+=',
|
accelerator: 'CommandOrControl+Plus',
|
||||||
click(item, focusedWindow) {
|
click(item, focusedWindow) {
|
||||||
if (focusedWindow) {
|
if (focusedWindow) {
|
||||||
AppMenu.sendAction('zoomIn');
|
AppMenu.sendAction('zoomIn');
|
||||||
@@ -121,7 +121,7 @@ class AppMenu {
|
|||||||
shell.openExternal('https://zulipchat.com/help/');
|
shell.openExternal('https://zulipchat.com/help/');
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
label: `${appName + 'Desktop'} - ${app.getVersion()}`,
|
label: `${appName + ' Desktop'} - ${app.getVersion()}`,
|
||||||
enabled: false
|
enabled: false
|
||||||
}, {
|
}, {
|
||||||
label: 'Report an Issue...',
|
label: 'Report an Issue...',
|
||||||
|
|||||||
31
app/package-lock.json
generated
31
app/package-lock.json
generated
@@ -101,6 +101,12 @@
|
|||||||
"tweetnacl": "0.14.5"
|
"tweetnacl": "0.14.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"bindings": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"bluebird": {
|
"bluebird": {
|
||||||
"version": "3.5.1",
|
"version": "3.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
|
||||||
@@ -318,6 +324,12 @@
|
|||||||
"resolved": "https://registry.npmjs.org/event-kit/-/event-kit-2.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/event-kit/-/event-kit-2.4.0.tgz",
|
||||||
"integrity": "sha512-ZXd9jxUoc/f/zdLdR3OUcCzT84WnpaNWefquLyE125akIC90sDs8S3T/qihliuVsaj7Osc0z8lLL2fjooE9Z4A=="
|
"integrity": "sha512-ZXd9jxUoc/f/zdLdR3OUcCzT84WnpaNWefquLyE125akIC90sDs8S3T/qihliuVsaj7Osc0z8lLL2fjooE9Z4A=="
|
||||||
},
|
},
|
||||||
|
"event-target-shim": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-1.1.1.tgz",
|
||||||
|
"integrity": "sha1-qG5e5r2qFgVEddp5fM3fDFVphJE=",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"extend": {
|
"extend": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
|
||||||
@@ -622,6 +634,25 @@
|
|||||||
"mkdirp": "0.5.1"
|
"mkdirp": "0.5.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node-mac-notifier": {
|
||||||
|
"version": "0.0.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-mac-notifier/-/node-mac-notifier-0.0.13.tgz",
|
||||||
|
"integrity": "sha1-1kt27RgfR5XURFui060Nb3KY9+I=",
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"bindings": "1.3.0",
|
||||||
|
"event-target-shim": "1.1.1",
|
||||||
|
"node-uuid": "1.4.8"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"node-uuid": {
|
||||||
|
"version": "1.4.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz",
|
||||||
|
"integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=",
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"oauth-sign": {
|
"oauth-sign": {
|
||||||
"version": "0.8.2",
|
"version": "0.8.2",
|
||||||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "zulip",
|
"name": "zulip",
|
||||||
"productName": "Zulip",
|
"productName": "Zulip",
|
||||||
"version": "1.7.0",
|
"version": "1.8.1",
|
||||||
"description": "Zulip Desktop App",
|
"description": "Zulip Desktop App",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"copyright": "Kandra Labs, Inc.",
|
"copyright": "Kandra Labs, Inc.",
|
||||||
@@ -30,10 +30,13 @@
|
|||||||
"electron-is-dev": "0.3.0",
|
"electron-is-dev": "0.3.0",
|
||||||
"electron-log": "2.2.7",
|
"electron-log": "2.2.7",
|
||||||
"electron-spellchecker": "1.1.2",
|
"electron-spellchecker": "1.1.2",
|
||||||
|
"electron-updater": "2.18.2",
|
||||||
"electron-window-state": "4.1.1",
|
"electron-window-state": "4.1.1",
|
||||||
"electron-updater": "2.16.2",
|
|
||||||
"node-json-db": "0.7.3",
|
"node-json-db": "0.7.3",
|
||||||
"request": "2.81.0",
|
"request": "2.81.0",
|
||||||
"wurl": "2.5.0"
|
"wurl": "2.5.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"node-mac-notifier": "0.0.13"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -263,7 +263,13 @@ img.server-info-icon {
|
|||||||
margin: 10px 0 20px 0;
|
margin: 10px 0 20px 0;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
width: 70%;
|
width: 70%;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-card:hover {
|
||||||
border-left: 8px solid #bcbcbc;
|
border-left: 8px solid #bcbcbc;
|
||||||
|
box-shadow: 0 2px 5px 0 rgba(0,0,0,0.16),
|
||||||
|
0 2px 0px 0px rgba(0,0,0,0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
.hidden {
|
.hidden {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ const Tab = require(__dirname + '/../components/tab.js');
|
|||||||
|
|
||||||
class FunctionalTab extends Tab {
|
class FunctionalTab extends Tab {
|
||||||
template() {
|
template() {
|
||||||
return `<div class="tab functional-tab">
|
return `<div class="tab functional-tab" data-tab-id="${this.props.tabIndex}">
|
||||||
<div class="server-tab-badge close-button">
|
<div class="server-tab-badge close-button">
|
||||||
<i class="material-icons">close</i>
|
<i class="material-icons">close</i>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const {ipcRenderer} = require('electron');
|
|||||||
|
|
||||||
class ServerTab extends Tab {
|
class ServerTab extends Tab {
|
||||||
template() {
|
template() {
|
||||||
return `<div class="tab">
|
return `<div class="tab" data-tab-id="${this.props.tabIndex}">
|
||||||
<div class="server-tooltip" style="display:none"></div>
|
<div class="server-tooltip" style="display:none"></div>
|
||||||
<div class="server-tab-badge"></div>
|
<div class="server-tab-badge"></div>
|
||||||
<div class="server-tab">
|
<div class="server-tab">
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ const path = require('path');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
const DomainUtil = require(__dirname + '/../utils/domain-util.js');
|
const DomainUtil = require(__dirname + '/../utils/domain-util.js');
|
||||||
|
const ConfigUtil = require(__dirname + '/../utils/config-util.js');
|
||||||
const SystemUtil = require(__dirname + '/../utils/system-util.js');
|
const SystemUtil = require(__dirname + '/../utils/system-util.js');
|
||||||
const LinkUtil = require(__dirname + '/../utils/link-util.js');
|
const LinkUtil = require(__dirname + '/../utils/link-util.js');
|
||||||
const { shell, app } = require('electron').remote;
|
const { shell, app } = require('electron').remote;
|
||||||
|
|
||||||
const BaseComponent = require(__dirname + '/../components/base.js');
|
const BaseComponent = require(__dirname + '/../components/base.js');
|
||||||
|
|
||||||
|
const shouldSilentWebview = ConfigUtil.getConfigItem('silent');
|
||||||
class WebView extends BaseComponent {
|
class WebView extends BaseComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super();
|
super();
|
||||||
@@ -24,6 +26,7 @@ class WebView extends BaseComponent {
|
|||||||
template() {
|
template() {
|
||||||
return `<webview
|
return `<webview
|
||||||
class="disabled"
|
class="disabled"
|
||||||
|
data-tab-id="${this.props.tabIndex}"
|
||||||
src="${this.props.url}"
|
src="${this.props.url}"
|
||||||
${this.props.nodeIntegration ? 'nodeIntegration' : ''}
|
${this.props.nodeIntegration ? 'nodeIntegration' : ''}
|
||||||
disablewebsecurity
|
disablewebsecurity
|
||||||
@@ -54,6 +57,12 @@ class WebView extends BaseComponent {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (shouldSilentWebview) {
|
||||||
|
this.$el.addEventListener('dom-ready', () => {
|
||||||
|
this.$el.setAudioMuted(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.$el.addEventListener('page-title-updated', event => {
|
this.$el.addEventListener('page-title-updated', event => {
|
||||||
const { title } = event;
|
const { title } = event;
|
||||||
this.badgeCount = this.getBadgeCount(title);
|
this.badgeCount = this.getBadgeCount(title);
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
const NodeConsole = require('console').Console;
|
|
||||||
const fs = require('fs');
|
|
||||||
|
|
||||||
const { app } = require('electron').remote;
|
|
||||||
const isDev = require('electron-is-dev');
|
|
||||||
|
|
||||||
const browserConsole = console;
|
|
||||||
const logDir = `${app.getPath('userData')}/Logs`;
|
|
||||||
if (!fs.existsSync(logDir)) {
|
|
||||||
fs.mkdirSync(logDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
function customConsole(opts, type, ...args) {
|
|
||||||
const { nodeConsole, timestamp } = opts;
|
|
||||||
if (timestamp) {
|
|
||||||
args.unshift(timestamp());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isDev) {
|
|
||||||
const nodeConsoleLog = nodeConsole[type] || nodeConsole.log;
|
|
||||||
nodeConsoleLog.apply(null, args);
|
|
||||||
}
|
|
||||||
browserConsole[type].apply(null, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTimestamp() {
|
|
||||||
const date = new Date();
|
|
||||||
const timestamp =
|
|
||||||
`${date.getMonth()}/${date.getDate()} ` +
|
|
||||||
`${date.getMinutes()}:${date.getSeconds()}`;
|
|
||||||
return timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setConsoleProto(type) {
|
|
||||||
Object.defineProperty(this, type, {
|
|
||||||
value(...args) {
|
|
||||||
const { timestamp, nodeConsole } = this;
|
|
||||||
const opts = {
|
|
||||||
timestamp,
|
|
||||||
nodeConsole
|
|
||||||
};
|
|
||||||
customConsole.apply(null, [].concat(opts, type, args));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class Console {
|
|
||||||
constructor(opts = {}) {
|
|
||||||
let { timestamp, file } = opts;
|
|
||||||
file = `${logDir}/${file || 'console.log'}`;
|
|
||||||
if (timestamp === true) {
|
|
||||||
timestamp = getTimestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileStream = fs.createWriteStream(file);
|
|
||||||
const nodeConsole = new NodeConsole(fileStream);
|
|
||||||
this.nodeConsole = nodeConsole;
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
this.setUpConsole();
|
|
||||||
}
|
|
||||||
|
|
||||||
setUpConsole() {
|
|
||||||
for (const type in browserConsole) {
|
|
||||||
setConsoleProto.call(this, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Console;
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
require(__dirname + '/js/tray.js');
|
|
||||||
const { ipcRenderer, remote } = require('electron');
|
const { ipcRenderer, remote } = require('electron');
|
||||||
|
|
||||||
const { session } = remote;
|
const { session } = remote;
|
||||||
|
|
||||||
|
require(__dirname + '/js/tray.js');
|
||||||
const DomainUtil = require(__dirname + '/js/utils/domain-util.js');
|
const DomainUtil = require(__dirname + '/js/utils/domain-util.js');
|
||||||
const WebView = require(__dirname + '/js/components/webview.js');
|
const WebView = require(__dirname + '/js/components/webview.js');
|
||||||
const ServerTab = require(__dirname + '/js/components/server-tab.js');
|
const ServerTab = require(__dirname + '/js/components/server-tab.js');
|
||||||
@@ -35,6 +35,7 @@ class ServerManagerView {
|
|||||||
this.activeTabIndex = -1;
|
this.activeTabIndex = -1;
|
||||||
this.tabs = [];
|
this.tabs = [];
|
||||||
this.functionalTabs = {};
|
this.functionalTabs = {};
|
||||||
|
this.tabIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
@@ -77,6 +78,7 @@ class ServerManagerView {
|
|||||||
showSidebar: true,
|
showSidebar: true,
|
||||||
badgeOption: true,
|
badgeOption: true,
|
||||||
startAtLogin: false,
|
startAtLogin: false,
|
||||||
|
startMinimized: false,
|
||||||
enableSpellchecker: true,
|
enableSpellchecker: true,
|
||||||
showNotification: true,
|
showNotification: true,
|
||||||
betaUpdate: false,
|
betaUpdate: false,
|
||||||
@@ -84,6 +86,13 @@ class ServerManagerView {
|
|||||||
lastActiveTab: 0
|
lastActiveTab: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Platform specific settings
|
||||||
|
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
// Only available on Windows
|
||||||
|
settingOptions.flashTaskbarOnMessage = true;
|
||||||
|
}
|
||||||
|
|
||||||
for (const i in settingOptions) {
|
for (const i in settingOptions) {
|
||||||
if (ConfigUtil.getConfigItem(i) === null) {
|
if (ConfigUtil.getConfigItem(i) === null) {
|
||||||
ConfigUtil.setConfigItem(i, settingOptions[i]);
|
ConfigUtil.setConfigItem(i, settingOptions[i]);
|
||||||
@@ -112,17 +121,20 @@ class ServerManagerView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
initServer(server, index) {
|
initServer(server, index) {
|
||||||
|
const tabIndex = this.getTabIndex();
|
||||||
this.tabs.push(new ServerTab({
|
this.tabs.push(new ServerTab({
|
||||||
role: 'server',
|
role: 'server',
|
||||||
icon: server.icon,
|
icon: server.icon,
|
||||||
$root: this.$tabsContainer,
|
$root: this.$tabsContainer,
|
||||||
onClick: this.activateLastTab.bind(this, index),
|
onClick: this.activateLastTab.bind(this, index),
|
||||||
index,
|
index,
|
||||||
|
tabIndex,
|
||||||
onHover: this.onHover.bind(this, index, server.alias),
|
onHover: this.onHover.bind(this, index, server.alias),
|
||||||
onHoverOut: this.onHoverOut.bind(this, index),
|
onHoverOut: this.onHoverOut.bind(this, index),
|
||||||
webview: new WebView({
|
webview: new WebView({
|
||||||
$root: this.$webviewsContainer,
|
$root: this.$webviewsContainer,
|
||||||
index,
|
index,
|
||||||
|
tabIndex,
|
||||||
url: server.url,
|
url: server.url,
|
||||||
name: server.alias,
|
name: server.alias,
|
||||||
isActive: () => {
|
isActive: () => {
|
||||||
@@ -147,11 +159,24 @@ class ServerManagerView {
|
|||||||
this.openSettings('General');
|
this.openSettings('General');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const $serverImgs = document.querySelectorAll('.server-icons');
|
||||||
|
$serverImgs.forEach($serverImg => {
|
||||||
|
$serverImg.addEventListener('error', () => {
|
||||||
|
$serverImg.src = 'img/icon.png';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
this.sidebarHoverEvent(this.$addServerButton, this.$addServerTooltip);
|
this.sidebarHoverEvent(this.$addServerButton, this.$addServerTooltip);
|
||||||
this.sidebarHoverEvent(this.$settingsButton, this.$settingsTooltip);
|
this.sidebarHoverEvent(this.$settingsButton, this.$settingsTooltip);
|
||||||
this.sidebarHoverEvent(this.$reloadButton, this.$reloadTooltip);
|
this.sidebarHoverEvent(this.$reloadButton, this.$reloadTooltip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTabIndex() {
|
||||||
|
const currentIndex = this.tabIndex;
|
||||||
|
this.tabIndex++;
|
||||||
|
return currentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
sidebarHoverEvent(SidebarButton, SidebarTooltip) {
|
sidebarHoverEvent(SidebarButton, SidebarTooltip) {
|
||||||
SidebarButton.addEventListener('mouseover', () => {
|
SidebarButton.addEventListener('mouseover', () => {
|
||||||
SidebarTooltip.removeAttribute('style');
|
SidebarTooltip.removeAttribute('style');
|
||||||
@@ -178,16 +203,19 @@ class ServerManagerView {
|
|||||||
|
|
||||||
this.functionalTabs[tabProps.name] = this.tabs.length;
|
this.functionalTabs[tabProps.name] = this.tabs.length;
|
||||||
|
|
||||||
|
const tabIndex = this.getTabIndex();
|
||||||
this.tabs.push(new FunctionalTab({
|
this.tabs.push(new FunctionalTab({
|
||||||
role: 'function',
|
role: 'function',
|
||||||
materialIcon: tabProps.materialIcon,
|
materialIcon: tabProps.materialIcon,
|
||||||
$root: this.$tabsContainer,
|
$root: this.$tabsContainer,
|
||||||
index: this.functionalTabs[tabProps.name],
|
index: this.functionalTabs[tabProps.name],
|
||||||
|
tabIndex,
|
||||||
onClick: this.activateTab.bind(this, this.functionalTabs[tabProps.name]),
|
onClick: this.activateTab.bind(this, this.functionalTabs[tabProps.name]),
|
||||||
onDestroy: this.destroyTab.bind(this, tabProps.name, this.functionalTabs[tabProps.name]),
|
onDestroy: this.destroyTab.bind(this, tabProps.name, this.functionalTabs[tabProps.name]),
|
||||||
webview: new WebView({
|
webview: new WebView({
|
||||||
$root: this.$webviewsContainer,
|
$root: this.$webviewsContainer,
|
||||||
index: this.functionalTabs[tabProps.name],
|
index: this.functionalTabs[tabProps.name],
|
||||||
|
tabIndex,
|
||||||
url: tabProps.url,
|
url: tabProps.url,
|
||||||
name: tabProps.name,
|
name: tabProps.name,
|
||||||
isActive: () => {
|
isActive: () => {
|
||||||
@@ -255,6 +283,27 @@ class ServerManagerView {
|
|||||||
tabs: this.tabs,
|
tabs: this.tabs,
|
||||||
activeTabIndex: this.activeTabIndex
|
activeTabIndex: this.activeTabIndex
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('toggle-sidebar', (event, state) => {
|
||||||
|
const selector = 'webview:not([class*=disabled])';
|
||||||
|
const webview = document.querySelector(selector);
|
||||||
|
const webContents = webview.getWebContents();
|
||||||
|
webContents.send('toggle-sidebar', state);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('toogle-silent', (event, state) => {
|
||||||
|
const webviews = document.querySelectorAll('webview');
|
||||||
|
webviews.forEach(webview => {
|
||||||
|
try {
|
||||||
|
webview.setAudioMuted(state);
|
||||||
|
} catch (err) {
|
||||||
|
// webview is not ready yet
|
||||||
|
webview.addEventListener('dom-ready', () => {
|
||||||
|
webview.isAudioMuted();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyTab(name, index) {
|
destroyTab(name, index) {
|
||||||
@@ -387,6 +436,18 @@ class ServerManagerView {
|
|||||||
this.$fullscreenPopup.classList.remove('show');
|
this.$fullscreenPopup.classList.remove('show');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('focus-webview-with-id', (event, webviewId) => {
|
||||||
|
const webviews = document.querySelectorAll('webview');
|
||||||
|
webviews.forEach(webview => {
|
||||||
|
const currentId = webview.getWebContents().id;
|
||||||
|
const tabId = webview.getAttribute('data-tab-id');
|
||||||
|
const concurrentTab = document.querySelector(`div[data-tab-id="${tabId}"]`);
|
||||||
|
if (currentId === webviewId) {
|
||||||
|
concurrentTab.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
ipcRenderer.on('render-taskbar-icon', (event, messageCount) => {
|
ipcRenderer.on('render-taskbar-icon', (event, messageCount) => {
|
||||||
// Create a canvas from unread messagecounts
|
// Create a canvas from unread messagecounts
|
||||||
function createOverlayIcon(messageCount) {
|
function createOverlayIcon(messageCount) {
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const { remote } = require('electron');
|
|
||||||
|
|
||||||
const ConfigUtil = require(__dirname + '/utils/config-util.js');
|
|
||||||
|
|
||||||
const app = remote.app;
|
|
||||||
|
|
||||||
// From https://github.com/felixrieseberg/electron-windows-notifications#appusermodelid
|
|
||||||
// On windows 8 we have to explicitly set the appUserModelId otherwise notification won't work.
|
|
||||||
app.setAppUserModelId('org.zulip.zulip-electron');
|
|
||||||
|
|
||||||
const NativeNotification = window.Notification;
|
|
||||||
|
|
||||||
class baseNotification extends NativeNotification {
|
|
||||||
constructor(title, opts) {
|
|
||||||
opts.silent = ConfigUtil.getConfigItem('silent') || false;
|
|
||||||
super(title, opts);
|
|
||||||
}
|
|
||||||
static requestPermission() {
|
|
||||||
return; // eslint-disable-line no-useless-return
|
|
||||||
}
|
|
||||||
// Override default Notification permission
|
|
||||||
static get permission() {
|
|
||||||
return ConfigUtil.getConfigItem('showNotification') ? 'granted' : 'denied';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.Notification = baseNotification;
|
|
||||||
|
|
||||||
100
app/renderer/js/notification/darwin-notifications.js
Normal file
100
app/renderer/js/notification/darwin-notifications.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { ipcRenderer } = require('electron');
|
||||||
|
const url = require('url');
|
||||||
|
const MacNotifier = require('node-mac-notifier');
|
||||||
|
const ConfigUtil = require('../utils/config-util');
|
||||||
|
const {
|
||||||
|
appId, customReply, focusCurrentServer, parseReply, setupReply
|
||||||
|
} = require('./helpers');
|
||||||
|
|
||||||
|
let replyHandler;
|
||||||
|
let clickHandler;
|
||||||
|
class DarwinNotification {
|
||||||
|
constructor(title, opts) {
|
||||||
|
const silent = ConfigUtil.getConfigItem('silent') || false;
|
||||||
|
const { host, protocol } = location;
|
||||||
|
const { icon } = opts;
|
||||||
|
const profilePic = url.resolve(`${protocol}//${host}`, icon);
|
||||||
|
|
||||||
|
this.tag = opts.tag;
|
||||||
|
const notification = new MacNotifier(title, Object.assign(opts, {
|
||||||
|
bundleId: appId,
|
||||||
|
canReply: true,
|
||||||
|
silent,
|
||||||
|
icon: profilePic
|
||||||
|
}));
|
||||||
|
|
||||||
|
notification.addEventListener('click', () => {
|
||||||
|
// focus to the server who sent the
|
||||||
|
// notification if not focused already
|
||||||
|
if (clickHandler) {
|
||||||
|
clickHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
focusCurrentServer();
|
||||||
|
ipcRenderer.send('focus-app');
|
||||||
|
});
|
||||||
|
|
||||||
|
notification.addEventListener('reply', this.notificationHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
static requestPermission() {
|
||||||
|
return; // eslint-disable-line no-useless-return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override default Notification permission
|
||||||
|
static get permission() {
|
||||||
|
return ConfigUtil.getConfigItem('showNotification') ? 'granted' : 'denied';
|
||||||
|
}
|
||||||
|
|
||||||
|
set onreply(handler) {
|
||||||
|
replyHandler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
get onreply() {
|
||||||
|
return replyHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
set onclick(handler) {
|
||||||
|
clickHandler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
get onclick() {
|
||||||
|
return clickHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
// not something that is common or
|
||||||
|
// used by zulip server but added to be
|
||||||
|
// future proff.
|
||||||
|
addEventListener(event, handler) {
|
||||||
|
if (event === 'click') {
|
||||||
|
clickHandler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event === 'reply') {
|
||||||
|
replyHandler = handler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationHandler({ response }) {
|
||||||
|
response = parseReply(response);
|
||||||
|
focusCurrentServer();
|
||||||
|
setupReply(this.tag);
|
||||||
|
|
||||||
|
if (replyHandler) {
|
||||||
|
replyHandler(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
customReply(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// method specific to notification api
|
||||||
|
// used by zulip
|
||||||
|
close() {
|
||||||
|
return; // eslint-disable-line no-useless-return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = DarwinNotification;
|
||||||
31
app/renderer/js/notification/default-notification.js
Normal file
31
app/renderer/js/notification/default-notification.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { ipcRenderer } = require('electron');
|
||||||
|
const ConfigUtil = require('../utils/config-util');
|
||||||
|
const { focusCurrentServer } = require('./helpers');
|
||||||
|
|
||||||
|
const NativeNotification = window.Notification;
|
||||||
|
class BaseNotification extends NativeNotification {
|
||||||
|
constructor(title, opts) {
|
||||||
|
opts.silent = true;
|
||||||
|
super(title, opts);
|
||||||
|
|
||||||
|
this.addEventListener('click', () => {
|
||||||
|
// focus to the server who sent the
|
||||||
|
// notification if not focused already
|
||||||
|
focusCurrentServer();
|
||||||
|
ipcRenderer.send('focus-app');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static requestPermission() {
|
||||||
|
return; // eslint-disable-line no-useless-return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override default Notification permission
|
||||||
|
static get permission() {
|
||||||
|
return ConfigUtil.getConfigItem('showNotification') ? 'granted' : 'denied';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = BaseNotification;
|
||||||
102
app/renderer/js/notification/helpers.js
Normal file
102
app/renderer/js/notification/helpers.js
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
const { remote } = require('electron');
|
||||||
|
|
||||||
|
// Do not change this
|
||||||
|
const appId = 'org.zulip.zulip-electron';
|
||||||
|
|
||||||
|
function checkElements(...elements) {
|
||||||
|
let status = true;
|
||||||
|
elements.forEach(element => {
|
||||||
|
if (element === null || element === undefined) {
|
||||||
|
status = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
function customReply(reply) {
|
||||||
|
// server does not support notification reply yet.
|
||||||
|
const buttonSelector = '.messagebox #send_controls button[type=submit]';
|
||||||
|
const messageboxSelector = '.selected_message .messagebox .messagebox-border .messagebox-content';
|
||||||
|
const textarea = document.querySelector('#compose-textarea');
|
||||||
|
const messagebox = document.querySelector(messageboxSelector);
|
||||||
|
const sendButton = document.querySelector(buttonSelector);
|
||||||
|
|
||||||
|
// sanity check for old server versions
|
||||||
|
const elementsExists = checkElements(textarea, messagebox, sendButton);
|
||||||
|
if (!elementsExists) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea.value = reply;
|
||||||
|
messagebox.click();
|
||||||
|
sendButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentWindow = remote.getCurrentWindow();
|
||||||
|
const webContents = remote.getCurrentWebContents();
|
||||||
|
const webContentsId = webContents.id;
|
||||||
|
|
||||||
|
// this function will focus the server that sent
|
||||||
|
// the notification. Main function implemented in main.js
|
||||||
|
function focusCurrentServer() {
|
||||||
|
currentWindow.send('focus-webview-with-id', webContentsId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this function parses the reply from to notification
|
||||||
|
// making it easier to reply from notification eg
|
||||||
|
// @username in reply will be converted to @**username**
|
||||||
|
// #stream in reply will be converted to #**stream**
|
||||||
|
// bot mentions are not yet supported
|
||||||
|
function parseReply(reply) {
|
||||||
|
const usersDiv = document.querySelectorAll('#user_presences li');
|
||||||
|
const streamHolder = document.querySelectorAll('#stream_filters li');
|
||||||
|
const users = [];
|
||||||
|
const streams = [];
|
||||||
|
|
||||||
|
usersDiv.forEach(userRow => {
|
||||||
|
const anchor = userRow.querySelector('span a');
|
||||||
|
if (anchor !== null) {
|
||||||
|
const user = `@${anchor.textContent.trim()}`;
|
||||||
|
const mention = `@**${user.replace(/^@/, '')}**`;
|
||||||
|
users.push([user, mention]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
streamHolder.forEach(stream => {
|
||||||
|
const streamAnchor = stream.querySelector('div a');
|
||||||
|
if (streamAnchor !== null) {
|
||||||
|
const streamName = `#${streamAnchor.textContent.trim()}`;
|
||||||
|
const streamMention = `#**${streamName.replace(/^#/, '')}**`;
|
||||||
|
streams.push([streamName, streamMention]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
users.forEach(([user, mention]) => {
|
||||||
|
if (reply.includes(user)) {
|
||||||
|
const regex = new RegExp(user, 'g');
|
||||||
|
reply = reply.replace(regex, mention);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
streams.forEach(([stream, streamMention]) => {
|
||||||
|
const regex = new RegExp(stream, 'g');
|
||||||
|
reply = reply.replace(regex, streamMention);
|
||||||
|
});
|
||||||
|
|
||||||
|
reply = reply.replace(/\\n/, '\n');
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupReply(id) {
|
||||||
|
const { narrow } = window;
|
||||||
|
narrow.by_subject(id, { trigger: 'notification' });
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
appId,
|
||||||
|
checkElements,
|
||||||
|
customReply,
|
||||||
|
parseReply,
|
||||||
|
setupReply,
|
||||||
|
focusCurrentServer
|
||||||
|
};
|
||||||
18
app/renderer/js/notification/index.js
Normal file
18
app/renderer/js/notification/index.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const {
|
||||||
|
remote: { app }
|
||||||
|
} = require('electron');
|
||||||
|
|
||||||
|
const DefaultNotification = require('./default-notification');
|
||||||
|
const { appId } = require('./helpers');
|
||||||
|
|
||||||
|
// From https://github.com/felixrieseberg/electron-windows-notifications#appusermodelid
|
||||||
|
// On windows 8 we have to explicitly set the appUserModelId otherwise notification won't work.
|
||||||
|
app.setAppUserModelId(appId);
|
||||||
|
|
||||||
|
window.Notification = DefaultNotification;
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
const DarwinNotification = require('./darwin-notifications');
|
||||||
|
window.Notification = DarwinNotification;
|
||||||
|
}
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const { ipcRenderer, remote } = require('electron');
|
||||||
const { ipcRenderer } = require('electron');
|
|
||||||
const { app, dialog } = require('electron').remote;
|
|
||||||
|
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
|
|
||||||
|
const { app, dialog } = remote;
|
||||||
|
const currentBrowserWindow = remote.getCurrentWindow();
|
||||||
const BaseSection = require(__dirname + '/base-section.js');
|
const BaseSection = require(__dirname + '/base-section.js');
|
||||||
const ConfigUtil = require(__dirname + '/../../utils/config-util.js');
|
const ConfigUtil = require(__dirname + '/../../utils/config-util.js');
|
||||||
|
|
||||||
@@ -61,10 +60,14 @@ class GeneralSection extends BaseSection {
|
|||||||
<div class="setting-description">Start app at login</div>
|
<div class="setting-description">Start app at login</div>
|
||||||
<div class="setting-control"></div>
|
<div class="setting-control"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="setting-row" id="start-minimize-option">
|
||||||
|
<div class="setting-description">Always start minimized</div>
|
||||||
|
<div class="setting-control"></div>
|
||||||
|
</div>
|
||||||
<div class="setting-row" id="enable-spellchecker-option">
|
<div class="setting-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 class="setting-control"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="title">Reset Application Data</div>
|
<div class="title">Reset Application Data</div>
|
||||||
<div class="settings-card">
|
<div class="settings-card">
|
||||||
@@ -89,6 +92,7 @@ class GeneralSection extends BaseSection {
|
|||||||
this.updateResetDataOption();
|
this.updateResetDataOption();
|
||||||
this.showDesktopNotification();
|
this.showDesktopNotification();
|
||||||
this.enableSpellchecker();
|
this.enableSpellchecker();
|
||||||
|
this.minimizeOnStart();
|
||||||
|
|
||||||
// Platform specific settings
|
// Platform specific settings
|
||||||
// Flashing taskbar on Windows
|
// Flashing taskbar on Windows
|
||||||
@@ -155,6 +159,7 @@ class GeneralSection extends BaseSection {
|
|||||||
const newValue = !ConfigUtil.getConfigItem('silent', true);
|
const newValue = !ConfigUtil.getConfigItem('silent', true);
|
||||||
ConfigUtil.setConfigItem('silent', newValue);
|
ConfigUtil.setConfigItem('silent', newValue);
|
||||||
this.updateSilentOption();
|
this.updateSilentOption();
|
||||||
|
currentBrowserWindow.send('toogle-silent', newValue);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -234,6 +239,18 @@ class GeneralSection extends BaseSection {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
minimizeOnStart() {
|
||||||
|
this.generateSettingOption({
|
||||||
|
$element: document.querySelector('#start-minimize-option .setting-control'),
|
||||||
|
value: ConfigUtil.getConfigItem('startMinimized', false),
|
||||||
|
clickHandler: () => {
|
||||||
|
const newValue = !ConfigUtil.getConfigItem('startMinimized');
|
||||||
|
ConfigUtil.setConfigItem('startMinimized', newValue);
|
||||||
|
this.minimizeOnStart();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = GeneralSection;
|
module.exports = GeneralSection;
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ class NewServerForm extends BaseComponent {
|
|||||||
return `
|
return `
|
||||||
<div class="settings-card">
|
<div class="settings-card">
|
||||||
<div class="server-info-right">
|
<div class="server-info-right">
|
||||||
<div class="title">URL of your Zulip organization</div>
|
<div class="title">URL of Zulip organization</div>
|
||||||
<div class="server-info-row">
|
<div class="server-info-row">
|
||||||
<input class="setting-input-value" autofocus placeholder="acme.zulipchat.com"/>
|
<input class="setting-input-value" autofocus placeholder="your-organization.zulipchat.com or chat.your-organization.com"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="server-info-row">
|
<div class="server-info-row">
|
||||||
<div class="action blue server-save-action">
|
<div class="action blue server-save-action">
|
||||||
|
|||||||
@@ -73,6 +73,18 @@ class PreferenceView extends BaseComponent {
|
|||||||
ipcRenderer.on('switch-settings-nav', (event, navItem) => {
|
ipcRenderer.on('switch-settings-nav', (event, navItem) => {
|
||||||
this.handleNavigation(navItem);
|
this.handleNavigation(navItem);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('toggle-sidebar', (event, state) => {
|
||||||
|
const inputSelector = '#sidebar-option .action .switch input';
|
||||||
|
const input = document.querySelector(inputSelector);
|
||||||
|
input.checked = state;
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcRenderer.on('toggletray', (event, state) => {
|
||||||
|
const inputSelector = '#tray-option .action .switch input';
|
||||||
|
const input = document.querySelector(inputSelector);
|
||||||
|
input.checked = state;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -197,13 +197,16 @@ ipcRenderer.on('tray', (event, arg) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function toggleTray() {
|
function toggleTray() {
|
||||||
|
let state;
|
||||||
if (window.tray) {
|
if (window.tray) {
|
||||||
|
state = false;
|
||||||
window.tray.destroy();
|
window.tray.destroy();
|
||||||
if (window.tray.isDestroyed()) {
|
if (window.tray.isDestroyed()) {
|
||||||
window.tray = null;
|
window.tray = null;
|
||||||
}
|
}
|
||||||
ConfigUtil.setConfigItem('trayIcon', false);
|
ConfigUtil.setConfigItem('trayIcon', false);
|
||||||
} else {
|
} else {
|
||||||
|
state = true;
|
||||||
createTray();
|
createTray();
|
||||||
if (process.platform === 'linux' || process.platform === 'win32') {
|
if (process.platform === 'linux' || process.platform === 'win32') {
|
||||||
renderNativeImage(unread).then(image => {
|
renderNativeImage(unread).then(image => {
|
||||||
@@ -213,6 +216,10 @@ function toggleTray() {
|
|||||||
}
|
}
|
||||||
ConfigUtil.setConfigItem('trayIcon', true);
|
ConfigUtil.setConfigItem('trayIcon', true);
|
||||||
}
|
}
|
||||||
|
const selector = 'webview:not([class*=disabled])';
|
||||||
|
const webview = document.querySelector(selector);
|
||||||
|
const webContents = webview.getWebContents();
|
||||||
|
webContents.send('toggletray', state);
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcRenderer.on('toggletray', toggleTray);
|
ipcRenderer.on('toggletray', toggleTray);
|
||||||
|
|||||||
@@ -1,16 +1,29 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
const process = require('process');
|
const process = require('process');
|
||||||
const JsonDB = require('node-json-db');
|
const JsonDB = require('node-json-db');
|
||||||
|
const Logger = require('./logger-util');
|
||||||
|
|
||||||
|
const logger = new Logger({
|
||||||
|
file: 'config-util.log',
|
||||||
|
timestamp: true
|
||||||
|
});
|
||||||
|
|
||||||
let instance = null;
|
let instance = null;
|
||||||
|
let dialog = null;
|
||||||
let app = null;
|
let app = null;
|
||||||
|
|
||||||
/* To make the util runnable in both main and renderer process */
|
/* To make the util runnable in both main and renderer process */
|
||||||
if (process.type === 'renderer') {
|
if (process.type === 'renderer') {
|
||||||
app = require('electron').remote.app;
|
const remote = require('electron').remote;
|
||||||
|
dialog = remote.dialog;
|
||||||
|
app = remote.app;
|
||||||
} else {
|
} else {
|
||||||
app = require('electron').app;
|
const electron = require('electron');
|
||||||
|
dialog = electron.dialog;
|
||||||
|
app = electron.app;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConfigUtil {
|
class ConfigUtil {
|
||||||
@@ -47,7 +60,22 @@ class ConfigUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reloadDB() {
|
reloadDB() {
|
||||||
this.db = new JsonDB(app.getPath('userData') + '/settings.json', true, true);
|
const settingsJsonPath = path.join(app.getPath('userData'), '/settings.json');
|
||||||
|
try {
|
||||||
|
const file = fs.readFileSync(settingsJsonPath, 'utf8');
|
||||||
|
JSON.parse(file);
|
||||||
|
} catch (err) {
|
||||||
|
if (fs.existsSync(settingsJsonPath)) {
|
||||||
|
fs.unlinkSync(settingsJsonPath);
|
||||||
|
dialog.showErrorBox(
|
||||||
|
'Error saving settings',
|
||||||
|
'We encountered error while saving current settings.'
|
||||||
|
);
|
||||||
|
logger.error('Error while JSON parsing settings.json: ');
|
||||||
|
logger.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.db = new JsonDB(settingsJsonPath, true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
31
app/renderer/js/utils/default-util.js
Normal file
31
app/renderer/js/utils/default-util.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
let app = null;
|
||||||
|
let setupCompleted = false;
|
||||||
|
if (process.type === 'renderer') {
|
||||||
|
app = require('electron').remote.app;
|
||||||
|
} else {
|
||||||
|
app = require('electron').app;
|
||||||
|
}
|
||||||
|
|
||||||
|
const zulipDir = app.getPath('userData');
|
||||||
|
const logDir = `${zulipDir}/Logs/`;
|
||||||
|
const initSetUp = () => {
|
||||||
|
// if it is the first time the app is running
|
||||||
|
// create zulip dir in userData folder to
|
||||||
|
// avoid errors
|
||||||
|
if (!setupCompleted) {
|
||||||
|
if (!fs.existsSync(zulipDir)) {
|
||||||
|
fs.mkdirSync(zulipDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(logDir)) {
|
||||||
|
fs.mkdirSync(logDir);
|
||||||
|
}
|
||||||
|
setupCompleted = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
initSetUp
|
||||||
|
};
|
||||||
@@ -5,6 +5,12 @@ const fs = require('fs');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const JsonDB = require('node-json-db');
|
const JsonDB = require('node-json-db');
|
||||||
const request = require('request');
|
const request = require('request');
|
||||||
|
const Logger = require('./logger-util');
|
||||||
|
|
||||||
|
const logger = new Logger({
|
||||||
|
file: `domain-util.log`,
|
||||||
|
timestamp: true
|
||||||
|
});
|
||||||
|
|
||||||
let instance = null;
|
let instance = null;
|
||||||
|
|
||||||
@@ -115,11 +121,16 @@ class DomainUtil {
|
|||||||
'Error: unable to verify the first certificate',
|
'Error: unable to verify the first certificate',
|
||||||
'Error: unable to get local issuer certificate'
|
'Error: unable to get local issuer certificate'
|
||||||
];
|
];
|
||||||
|
|
||||||
// If the domain contains following strings we just bypass the server
|
// If the domain contains following strings we just bypass the server
|
||||||
const whitelistDomains = [
|
const whitelistDomains = [
|
||||||
'zulipdev.org'
|
'zulipdev.org'
|
||||||
];
|
];
|
||||||
if (!error && response.statusCode !== 404) {
|
|
||||||
|
// make sure that error is a error or string not undefined
|
||||||
|
// so validation does not throw error.
|
||||||
|
error = error || '';
|
||||||
|
if (!error && response.statusCode < 400) {
|
||||||
// Correct
|
// Correct
|
||||||
this.getServerSettings(domain).then(serverSettings => {
|
this.getServerSettings(domain).then(serverSettings => {
|
||||||
resolve(serverSettings);
|
resolve(serverSettings);
|
||||||
@@ -159,7 +170,7 @@ class DomainUtil {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const invalidZulipServerError = `${domain} does not appear to be a valid Zulip server. Make sure that \
|
const invalidZulipServerError = `${domain} does not appear to be a valid Zulip server. Make sure that \
|
||||||
\n(1) you can connect to that URL in a web browser and \n (2) if you need a proxy to connect to the Internet, that you've configured your proxy in the Network settings`;
|
\n(1) you can connect to that URL in a web browser and \n (2) if you need a proxy to connect to the Internet, that you've configured your proxy in the Network settings \n (3) its a zulip server`;
|
||||||
reject(invalidZulipServerError);
|
reject(invalidZulipServerError);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -225,7 +236,23 @@ class DomainUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reloadDB() {
|
reloadDB() {
|
||||||
this.db = new JsonDB(app.getPath('userData') + '/domain.json', true, true);
|
const domainJsonPath = path.join(app.getPath('userData'), '/domain.json');
|
||||||
|
try {
|
||||||
|
const file = fs.readFileSync(domainJsonPath, 'utf8');
|
||||||
|
JSON.parse(file);
|
||||||
|
} catch (err) {
|
||||||
|
if (fs.existsSync(domainJsonPath)) {
|
||||||
|
fs.unlinkSync(domainJsonPath);
|
||||||
|
dialog.showErrorBox(
|
||||||
|
'Error saving new organization',
|
||||||
|
'There seems to be error while saving new organization, ' +
|
||||||
|
'you may have to re-add your previous organizations back.'
|
||||||
|
);
|
||||||
|
logger.error('Error while JSON parsing domain.json: ');
|
||||||
|
logger.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.db = new JsonDB(domainJsonPath, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
generateFilePath(url) {
|
generateFilePath(url) {
|
||||||
|
|||||||
87
app/renderer/js/utils/logger-util.js
Normal file
87
app/renderer/js/utils/logger-util.js
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
const NodeConsole = require('console').Console;
|
||||||
|
const fs = require('fs');
|
||||||
|
const isDev = require('electron-is-dev');
|
||||||
|
const { initSetUp } = require('./default-util');
|
||||||
|
|
||||||
|
initSetUp();
|
||||||
|
let app = null;
|
||||||
|
if (process.type === 'renderer') {
|
||||||
|
app = require('electron').remote.app;
|
||||||
|
} else {
|
||||||
|
app = require('electron').app;
|
||||||
|
}
|
||||||
|
|
||||||
|
const browserConsole = console;
|
||||||
|
const logDir = `${app.getPath('userData')}/Logs`;
|
||||||
|
|
||||||
|
class Logger {
|
||||||
|
constructor(opts = {}) {
|
||||||
|
let {
|
||||||
|
timestamp = true,
|
||||||
|
file = 'console.log',
|
||||||
|
level = true,
|
||||||
|
logInDevMode = false
|
||||||
|
} = opts;
|
||||||
|
|
||||||
|
file = `${logDir}/${file}`;
|
||||||
|
if (timestamp === true) {
|
||||||
|
timestamp = this.getTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileStream = fs.createWriteStream(file, { flags: 'a' });
|
||||||
|
const nodeConsole = new NodeConsole(fileStream);
|
||||||
|
|
||||||
|
this.nodeConsole = nodeConsole;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.level = level;
|
||||||
|
this.logInDevMode = logInDevMode;
|
||||||
|
this.setUpConsole();
|
||||||
|
}
|
||||||
|
|
||||||
|
_log(type, ...args) {
|
||||||
|
const {
|
||||||
|
nodeConsole, timestamp, level, logInDevMode
|
||||||
|
} = this;
|
||||||
|
let nodeConsoleLog;
|
||||||
|
|
||||||
|
/* eslint-disable no-fallthrough */
|
||||||
|
switch (true) {
|
||||||
|
case typeof timestamp === 'function':
|
||||||
|
args.unshift(timestamp() + ' |\t');
|
||||||
|
|
||||||
|
case (level !== false):
|
||||||
|
args.unshift(type.toUpperCase() + ' |');
|
||||||
|
|
||||||
|
case isDev || logInDevMode:
|
||||||
|
nodeConsoleLog = nodeConsole[type] || nodeConsole.log;
|
||||||
|
nodeConsoleLog.apply(null, args);
|
||||||
|
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
/* eslint-enable no-fallthrough */
|
||||||
|
|
||||||
|
browserConsole[type].apply(null, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpConsole() {
|
||||||
|
for (const type in browserConsole) {
|
||||||
|
this.setupConsoleMethod(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setupConsoleMethod(type) {
|
||||||
|
this[type] = (...args) => {
|
||||||
|
this._log(type, ...args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
getTimestamp() {
|
||||||
|
const date = new Date();
|
||||||
|
const timestamp =
|
||||||
|
`${date.getMonth()}/${date.getDate()} ` +
|
||||||
|
`${date.getMinutes()}:${date.getSeconds()}`;
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Logger;
|
||||||
@@ -20,4 +20,5 @@ install:
|
|||||||
build: off
|
build: off
|
||||||
|
|
||||||
test_script:
|
test_script:
|
||||||
- npm run test-all
|
- npm run test
|
||||||
|
- npm run test-e2e
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ gulp.task('reload:renderer', done => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('test-e2e', () => {
|
gulp.task('test-e2e', () => {
|
||||||
return gulp.src('tests/e2e/*.js')
|
return gulp.src('tests/*.js')
|
||||||
.pipe(tape({
|
.pipe(tape({
|
||||||
reporter: tapColorize()
|
reporter: tapColorize()
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
module.exports = function (config) {
|
|
||||||
config.set({
|
|
||||||
basePath: '',
|
|
||||||
frameworks: ['jasmine'],
|
|
||||||
browsers: ['Electron'],
|
|
||||||
preprocessors: {
|
|
||||||
'**/*.js': ['electron']
|
|
||||||
},
|
|
||||||
files: [
|
|
||||||
{pattern: './karma.shim.js', watched: true, included: true, served: true},
|
|
||||||
{pattern: './tests/unit/*.js', watched: true, included: true, served: true},
|
|
||||||
{pattern: './app/renderer/**/*.js', watched: true, included: false, served: true}
|
|
||||||
],
|
|
||||||
reporters: ['mocha'],
|
|
||||||
client: {
|
|
||||||
captureConsole: true,
|
|
||||||
useIframe: false
|
|
||||||
},
|
|
||||||
singleRun: true
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
window.require = window.parent.require;
|
|
||||||
window.process = window.parent.process;
|
|
||||||
window.__dirname = window.parent.__dirname;
|
|
||||||
require('module').globalPaths.push('./node_modules');
|
|
||||||
require('module').globalPaths.push('./app/renderer');
|
|
||||||
11
package-lock.json
generated
11
package-lock.json
generated
@@ -1561,9 +1561,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"electron": {
|
"electron": {
|
||||||
"version": "1.6.15",
|
"version": "1.7.9",
|
||||||
"resolved": "https://registry.npmjs.org/electron/-/electron-1.6.15.tgz",
|
"resolved": "https://registry.npmjs.org/electron/-/electron-1.7.9.tgz",
|
||||||
"integrity": "sha1-w07FRIa39Jpm21jG8koJKEFPrqc=",
|
"integrity": "sha1-rdVOn4+D7QL2UZ7BATX2mLGTNs8=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/node": "7.0.48",
|
"@types/node": "7.0.48",
|
||||||
@@ -5801,11 +5801,6 @@
|
|||||||
"resolve-from": "1.0.1"
|
"resolve-from": "1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"resemblejs": {
|
|
||||||
"version": "2.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/resemblejs/-/resemblejs-2.5.0.tgz",
|
|
||||||
"integrity": "sha512-j2p4H8jpxch3bG9SOoap4tWbNZ4hNGrl54y7WJSwoKBQM3ZBhVyaFEYqzlv06dY1I4Pns/M8Ten6R6+/jTjycA=="
|
|
||||||
},
|
|
||||||
"resolve": {
|
"resolve": {
|
||||||
"version": "1.5.0",
|
"version": "1.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz",
|
||||||
|
|||||||
22
package.json
22
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "zulip",
|
"name": "zulip",
|
||||||
"productName": "Zulip",
|
"productName": "Zulip",
|
||||||
"version": "1.7.0",
|
"version": "1.8.1",
|
||||||
"main": "./app/main",
|
"main": "./app/main",
|
||||||
"description": "Zulip Desktop App",
|
"description": "Zulip Desktop App",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
@@ -21,10 +21,8 @@
|
|||||||
"start": "electron app --disable-http-cache",
|
"start": "electron app --disable-http-cache",
|
||||||
"reinstall": "rm -rf node_modules; rm -rf app/node_modules; npm install",
|
"reinstall": "rm -rf node_modules; rm -rf app/node_modules; npm install",
|
||||||
"postinstall": "electron-builder install-app-deps",
|
"postinstall": "electron-builder install-app-deps",
|
||||||
"lint": "xo",
|
"test": "xo",
|
||||||
"test-e2e": "gulp test-e2e",
|
"test-e2e": "gulp test-e2e",
|
||||||
"test-unit": "karma start karma.conf.js",
|
|
||||||
"test-all": "xo && npm run test-unit && npm run test-e2e",
|
|
||||||
"dev": "gulp dev",
|
"dev": "gulp dev",
|
||||||
"pack": "electron-builder --dir",
|
"pack": "electron-builder --dir",
|
||||||
"dist": "electron-builder",
|
"dist": "electron-builder",
|
||||||
@@ -32,7 +30,7 @@
|
|||||||
"travis": "cd ./scripts && ./travis-build-test.sh"
|
"travis": "cd ./scripts && ./travis-build-test.sh"
|
||||||
},
|
},
|
||||||
"pre-commit": [
|
"pre-commit": [
|
||||||
"lint"
|
"test"
|
||||||
],
|
],
|
||||||
"build": {
|
"build": {
|
||||||
"appId": "org.zulip.zulip-electron",
|
"appId": "org.zulip.zulip-electron",
|
||||||
@@ -111,24 +109,17 @@
|
|||||||
"assert": "1.4.1",
|
"assert": "1.4.1",
|
||||||
"cp-file": "^5.0.0",
|
"cp-file": "^5.0.0",
|
||||||
"devtron": "1.4.0",
|
"devtron": "1.4.0",
|
||||||
"electron-builder": "19.46.4",
|
"electron-builder": "19.53.6",
|
||||||
"electron": "1.7.9",
|
"electron": "1.7.10",
|
||||||
"electron-connect": "0.6.2",
|
"electron-connect": "0.6.2",
|
||||||
"electron-debug": "1.4.0",
|
"electron-debug": "1.4.0",
|
||||||
"gulp": "3.9.1",
|
"gulp": "3.9.1",
|
||||||
"gulp-tape": "0.0.9",
|
"gulp-tape": "0.0.9",
|
||||||
"is-ci": "^1.0.10",
|
"is-ci": "^1.0.10",
|
||||||
"jasmine": "^2.8.0",
|
|
||||||
"karma": "^1.7.1",
|
|
||||||
"karma-electron": "^5.2.2",
|
|
||||||
"karma-electron-launcher": "^0.2.0",
|
|
||||||
"karma-jasmine": "^1.1.0",
|
|
||||||
"karma-mocha-reporter": "^2.2.5",
|
|
||||||
"pre-commit": "1.2.2",
|
"pre-commit": "1.2.2",
|
||||||
"spectron": "3.7.2",
|
"spectron": "3.7.2",
|
||||||
"tap-colorize": "^1.2.0",
|
"tap-colorize": "^1.2.0",
|
||||||
"tape": "^4.8.0",
|
"tape": "^4.8.0",
|
||||||
"watchify": "^3.9.0",
|
|
||||||
"xo": "0.18.2"
|
"xo": "0.18.2"
|
||||||
},
|
},
|
||||||
"xo": {
|
"xo": {
|
||||||
@@ -162,8 +153,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ignore": [
|
"ignore": [
|
||||||
"tests/e2e/*.js",
|
"tests/*.js"
|
||||||
"tests/unit/*"
|
|
||||||
],
|
],
|
||||||
"envs": [
|
"envs": [
|
||||||
"node",
|
"node",
|
||||||
|
|||||||
@@ -10,14 +10,8 @@ if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
|
|||||||
xdpyinfo | grep dimensions
|
xdpyinfo | grep dimensions
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# macOS
|
npm run test
|
||||||
# Run all the tests
|
|
||||||
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
|
||||||
npm run test-all
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Linux
|
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
||||||
# Only run linting test on Linux
|
npm run test-e2e
|
||||||
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
|
|
||||||
npm run lint
|
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ module.exports = {
|
|||||||
function createApp (t) {
|
function createApp (t) {
|
||||||
generateTestAppPackageJson()
|
generateTestAppPackageJson()
|
||||||
return new Application({
|
return new Application({
|
||||||
path: path.join(__dirname,'..', '..', 'node_modules', '.bin',
|
path: path.join(__dirname, '..', 'node_modules', '.bin',
|
||||||
'electron' + (process.platform === 'win32' ? '.cmd' : '')),
|
'electron' + (process.platform === 'win32' ? '.cmd' : '')),
|
||||||
args: [path.join(__dirname)], // Ensure this dir has a package.json file with a 'main' entry piont
|
args: [path.join(__dirname)], // Ensure this dir has a package.json file with a 'main' entry piont
|
||||||
env: {NODE_ENV: 'test'},
|
env: {NODE_ENV: 'test'},
|
||||||
@@ -34,9 +34,9 @@ function createApp (t) {
|
|||||||
// Reads app package.json and updates the productName to config.TEST_APP_PRODUCT_NAME
|
// Reads app package.json and updates the productName to config.TEST_APP_PRODUCT_NAME
|
||||||
// We do this so that the app integration doesn't doesn't share the same appDataDir as the dev application
|
// We do this so that the app integration doesn't doesn't share the same appDataDir as the dev application
|
||||||
function generateTestAppPackageJson () {
|
function generateTestAppPackageJson () {
|
||||||
let packageJson = require(path.join(__dirname, '..', '..', 'package.json'))
|
let packageJson = require(path.join(__dirname, '../package.json'))
|
||||||
packageJson.productName = config.TEST_APP_PRODUCT_NAME
|
packageJson.productName = config.TEST_APP_PRODUCT_NAME
|
||||||
packageJson.main = '../../app/main'
|
packageJson.main = '../app/main'
|
||||||
|
|
||||||
const testPackageJsonPath = path.join(__dirname, 'package.json')
|
const testPackageJsonPath = path.join(__dirname, 'package.json')
|
||||||
fs.writeFileSync(testPackageJsonPath, JSON.stringify(packageJson, null, ' '), 'utf-8')
|
fs.writeFileSync(testPackageJsonPath, JSON.stringify(packageJson, null, ' '), 'utf-8')
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
const ConfigUtil = require('js/utils/config-util.js');
|
|
||||||
const SetupSpellChecker = require('js/spellchecker')
|
|
||||||
|
|
||||||
describe('test spell checker', function () {
|
|
||||||
// enable spellchecker settings
|
|
||||||
ConfigUtil.setConfigItem('enableSpellchecker', true);
|
|
||||||
ConfigUtil.setConfigItem('spellcheckerLanguage', 'en');
|
|
||||||
|
|
||||||
SetupSpellChecker.init() // re-initialize after setting update
|
|
||||||
|
|
||||||
const spellCheckHandler = SetupSpellChecker.SpellCheckHandler
|
|
||||||
|
|
||||||
it('mark misspelled word', function () {
|
|
||||||
expect(spellCheckHandler.isMisspelled('helpe')).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('verify properly spelled word', function () {
|
|
||||||
expect(spellCheckHandler.isMisspelled('help')).toBe(false)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
Reference in New Issue
Block a user