Compare commits

...

40 Commits

Author SHA1 Message Date
Akash Nimare
5b34bc696e electron: Fix app not rendering colors properly.
There is a bug in v2 of electron which causes
pale colors. Resetting the color profile to srgb
fixes this problem. This is a temporary solution
until electron officially fixes this.

Fixes: #497.
2018-05-23 11:10:20 +05:30
Akash Nimare
0e3c6bceeb node: Update node to v8 on appveyor. 2018-05-22 01:03:54 +05:30
Akash Nimare
c2e138c16a tests: Temporarily disable e2e tests on appveyor. 2018-05-21 23:23:41 +05:30
Akash Nimare
8d1b027b0e tests: Temporarily disable e2e tests. 2018-05-21 21:52:23 +05:30
Akash Nimare
7bb8d78973 node: Update node to v8 on travis. 2018-05-21 04:08:58 +05:30
Akash Nimare
a7c7791bf8 new release v2.3.1. 2018-05-21 03:40:22 +05:30
Akash Nimare
5d120b4416 shortcuts-menu: Add a tip-section to link to webapp's built-in hotkeys. 2018-05-21 03:29:56 +05:30
Abhigyan Khaund
ceaf13dee2 shortcuts-menu: Add a tip-section to link to webapp's built-in hotkeys.
Fixes: #459
2018-05-21 03:27:30 +05:30
Akash Nimare
f81381dfec electron: Update electron to v2.0.1. 2018-05-21 03:18:18 +05:30
Akash Nimare
dbe89cdd09 security: Use proper method to escape html.
Now using the `escape-html` module so that we can decode
the realm description properly and escape the html at the
same time. The encodeURIComponent function doesn't
provide this kind of flexibility. We need to decode the
real description properly since we show the same in the
tooltip and setting page.
2018-05-21 03:11:03 +05:30
Priyank Patel
14c59bdae1 Update node modules.
* dependencies: Upgrade rc to the depth of 5.

This updates rc dependecies, to fix a vulnerability

deep-extend - Prototype Pollution
Refs:  https://nodesecurity.io/advisories/612

* dependencies: Upgrade stringstream to the depth of 6.

This solves, 5 vulnerabilities which were using the vulnerable
version of stringstream.

stringstream - Out-of-bounds Read
Refs: https://nodesecurity.io/advisories/664

* gulp: Upgrade gulp to v4.0.0.

This solves, 5 vulnerabilities.

minimatch - Regular Expression Denial of Service
https://nodesecurity.io/advisories/118

lodash - Prototype Pollution
https://nodesecurity.io/advisories/577

* dependencies: Update request to the depth of 6.

This solves 5 vulnerabilities.

stringstream - Out-of-bounds Read
https://nodesecurity.io/advisories/664
2018-05-21 02:17:18 +05:30
Akash Nimare
0ac3e3f6d3 New release v2.3.0. 2018-05-17 00:59:39 +05:30
Akash Nimare
d69c1339e6 security: Do proper HTML escaping for server data.
This commit fixes a security bug which was caused by
using innerHTML and not doing proper HTML escaping.
Ideally, we should be doing proper HTML escaping for
any data we get from the server to avoid XSS attack.
We already handle realm icon and url very well, the
realm description was not handled previously but this commit
now fixes this in a right way.
2018-05-16 18:25:03 +05:30
Akash Nimare
fe56a20334 setting: Add an option to disable the auto-updates. (#493)
Fixes: #491.
2018-05-16 16:00:21 +05:30
Akash Nimare
cfc97c9b73 Update electron-builder. 2018-05-15 19:01:33 +05:30
Abhigyan Khaund
2e70b515da menu: Add option to check for updates.
Fixes: #479.
2018-05-15 17:20:40 +05:30
Akash Nimare
51e414a508 windows: Update deprecated electron api (toPng -> toPNG). 2018-05-08 19:24:42 +05:30
Akash Nimare
8e7a9bf230 release: new beta release v2.2.0. 2018-05-08 03:23:52 +05:30
Akash Nimare
6493ddb8ec builder: Update electron-updater. 2018-05-08 03:21:53 +05:30
Akash Nimare
31edbe0d67 mac: Update dmg installer image.
Fixes: #474.
2018-05-08 03:17:31 +05:30
Abhigyan Khaund
9980fee785 loading-indicator: Fix loading indicator when server is loaded.
This PR changes the current implementation of the loading indicator by attaching
the indicator to the right dom element, so that it doesn't show up once a server
is loaded.

Fixes: #482.
2018-05-08 00:41:20 +05:30
Akash Nimare
ff9986ec6b crash-reporter: Remove deprecated autoSubmit api. 2018-05-03 04:15:20 +05:30
Akash Nimare
f3423d394c release: New beta release v2.1.0-beta. 2018-05-03 03:39:15 +05:30
Akash Nimare
a1da199627 Update app dependencies. 2018-05-03 03:09:53 +05:30
Abhigyan Khaund
537fbe8f9e feature: Add DND button in left sidebar.
This adds the do not disturb button to the left sidebar
which disables sound and notifications. It also disables
flash taskbar on windows.

Fixes: #298.
2018-05-03 02:35:13 +05:30
Akash Nimare
3fccb33fca electron. Update electron to v2.0.0. 2018-05-02 02:44:11 +05:30
Abhigyan Khaund
5638590c8b internal-links: Download file attachment links using downloadURL.
This commit download file attachments using downloadURL method of
webContent, the same way loadURL opens internal links. This
removes the use of hidden webview added in
f70432f4e3.

Improves: #469.
2018-05-01 20:37:33 +05:30
Priyank Patel
29ed00981d hidden-webview: Move hidden webview so it does't get deleted.
It turns out if you add/remove an org the hidden webview get deleted
since its in #webview-container where other sidebar webviews like which
get removed and readded through that process.

Improves: #469.
2018-05-01 06:09:36 +05:30
Akash Nimare
d7638c0b95 artifacts: Update artifacts name. 2018-05-01 04:14:40 +05:30
Akash Nimare
7fadbe877b electron: Update app dependencies.
electron: v1.8.6
electron-builder: v20.11.1
electron-updater: v2.21.8
2018-05-01 00:59:59 +05:30
Akash Nimare
32a21889fb snap: Update snap summary. 2018-04-20 18:55:45 +05:30
Akash Nimare
c4a961f9da snap: Update snap config. 2018-04-19 23:48:58 +05:30
Akash Nimare
ceaa898570 snap: Add source branch for snap. 2018-04-19 23:19:01 +05:30
Akash Nimare
73fe17041d snap: Update Snap configuration. 2018-04-19 22:11:33 +05:30
Akash Nimare
9f756cad3e snap: Update snap icon path. 2018-04-19 20:58:48 +05:30
Akash Nimare
6db6b7c482 reconnect-util: Do not throw error message on server page while reconnecting. 2018-04-18 22:00:30 +05:30
Akash Nimare
09c45e75e8 Update package-lock files. 2018-04-18 17:51:22 +05:30
Akash Nimare
120b80cf65 release: 🎉 v2.0.0. 2018-04-17 15:51:32 +05:30
Abhigyan Khaund
22f705960d sidebar: Add scrollbar for list of organizations on overflow. 2018-04-16 19:38:26 +05:30
Priyank Patel
ca8ce1deaa report-issue: Add report issue UX using send-feedback electron element.
This uses @electron-elements/send-feedback package to easily implement
UX for reporting issues.
2018-04-16 11:34:11 +05:30
29 changed files with 11930 additions and 98 deletions

View File

@@ -15,7 +15,7 @@ addons:
language: node_js
node_js:
- '6'
- '8'
before_install:
- ./scripts/travis-xvfb.sh

View File

@@ -1,11 +1,11 @@
'use strict';
const { app, dialog } = require('electron');
const { app, dialog, shell } = require('electron');
const { autoUpdater } = require('electron-updater');
const isDev = require('electron-is-dev');
const ConfigUtil = require('./../renderer/js/utils/config-util.js');
function appUpdater() {
function appUpdater(updateFromMenu = false) {
// Don't initiate auto-updates in development
if (isDev) {
return;
@@ -17,6 +17,8 @@ function appUpdater() {
return;
}
let updateAvailable = false;
// Create Logs directory
const LogsDir = `${app.getPath('userData')}/Logs`;
@@ -28,7 +30,58 @@ function appUpdater() {
autoUpdater.logger = log;
// Handle auto updates for beta/pre releases
autoUpdater.allowPrerelease = ConfigUtil.getConfigItem('betaUpdate') || false;
const isBetaUpdate = ConfigUtil.getConfigItem('betaUpdate');
autoUpdater.allowPrerelease = isBetaUpdate || false;
const eventsListenerRemove = ['update-available', 'update-not-available'];
autoUpdater.on('update-available', info => {
if (updateFromMenu) {
dialog.showMessageBox({
message: `A new version ${info.version}, of Zulip Desktop is available`,
detail: 'The update will be downloaded in the background. You will be notified when it is ready to be installed.'
});
updateAvailable = true;
// This is to prevent removal of 'update-downloaded' and 'error' event listener.
eventsListenerRemove.forEach(event => {
autoUpdater.removeAllListeners(event);
});
}
});
autoUpdater.on('update-not-available', () => {
if (updateFromMenu) {
dialog.showMessageBox({
message: 'No updates available',
detail: `You are running the latest version of Zulip Desktop.\nVersion: ${app.getVersion()}`
});
// Remove all autoUpdator listeners so that next time autoUpdator is manually called these
// listeners don't trigger multiple times.
autoUpdater.removeAllListeners();
}
});
autoUpdater.on('error', error => {
if (updateFromMenu) {
const messageText = (updateAvailable) ? ('Unable to download the updates') : ('Unable to check for updates');
dialog.showMessageBox({
type: 'error',
buttons: ['Manual Download', 'Cancel'],
message: messageText,
detail: (error).toString() + `\n\nThe latest version of Zulip Desktop is available at -\nhttps://zulipchat.com/apps/.\n
Current Version: ${app.getVersion()}`
}, response => {
if (response === 0) {
shell.openExternal('https://zulipchat.com/apps/');
}
});
// Remove all autoUpdator listeners so that next time autoUpdator is manually called these
// listeners don't trigger multiple times.
autoUpdater.removeAllListeners();
}
});
// Ask the user if update is available
// eslint-disable-next-line no-unused-vars

View File

@@ -7,7 +7,7 @@ const crashHandler = () => {
productName: 'zulip-electron',
companyName: 'Kandra Labs, Inc.',
submitURL: 'https://zulip-sentry.herokuapp.com/crashreport',
autoSubmit: true
uploadToServer: true
});
};

View File

@@ -131,6 +131,9 @@ function createMainWindow() {
// Decrease load on GPU (experimental)
app.disableHardwareAcceleration();
// Temporary fix for Electron render colors differently
app.commandLine.appendSwitch('force-color-profile', 'srgb');
// eslint-disable-next-line max-params
app.on('certificate-error', (event, webContents, url, error, certificate, callback) => {
event.preventDefault();
@@ -161,7 +164,9 @@ app.on('ready', () => {
page.once('did-frame-finish-load', () => {
// Initate auto-updates on MacOS and Windows
appUpdater();
if (ConfigUtil.getConfigItem('autoUpdate')) {
appUpdater();
}
crashHandler();
});

View File

@@ -1,12 +1,13 @@
'use strict';
const os = require('os');
const path = require('path');
const { app, shell, BrowserWindow, Menu, dialog } = require('electron');
const fs = require('fs-extra');
const { appUpdater } = require('./autoupdater');
const ConfigUtil = require(__dirname + '/../renderer/js/utils/config-util.js');
const DNDUtil = require(__dirname + '/../renderer/js/utils/dnd-util.js');
const appName = app.getName();
@@ -139,13 +140,11 @@ class AppMenu {
}, {
label: 'Report an Issue...',
click() {
const body = `
<!-- Please succinctly describe your issue and steps to reproduce it. -->
-
${app.getName()} ${app.getVersion()}
Electron ${process.versions.electron}
${process.platform} ${process.arch} ${os.release()}`;
shell.openExternal(`https://github.com/zulip/zulip-electron/issues/new?body=${encodeURIComponent(body)}`);
// the goal is to notify the main.html BrowserWindow
// which may not be the focused window.
BrowserWindow.getAllWindows().forEach(window => {
window.webContents.send('open-feedback-modal');
});
}
}];
}
@@ -197,7 +196,12 @@ class AppMenu {
AppMenu.sendAction('open-about');
}
}
}, {
}, {
label: `Check for Update`,
click() {
AppMenu.checkForUpdate();
}
}, {
type: 'separator'
}, {
label: 'Desktop App Settings',
@@ -217,6 +221,13 @@ 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: 'Reset App Settings',
accelerator: 'Command+Shift+D',
@@ -297,7 +308,12 @@ class AppMenu {
AppMenu.sendAction('open-about');
}
}
}, {
}, {
label: `Check for Update`,
click() {
AppMenu.checkForUpdate();
}
}, {
type: 'separator'
}, {
label: 'Desktop App Settings',
@@ -319,6 +335,13 @@ class AppMenu {
}
}, {
type: 'separator'
}, {
label: 'Toggle Do Not Disturb',
accelerator: 'Ctrl+Shift+M',
click() {
const dndUtil = DNDUtil.toggle();
AppMenu.sendAction('toggle-dnd', dndUtil.dnd, dndUtil.newSettings);
}
}, {
label: 'Reset App Settings',
accelerator: 'Ctrl+Shift+D',
@@ -387,6 +410,9 @@ class AppMenu {
win.webContents.send(action, ...params);
}
static checkForUpdate() {
appUpdater(true);
}
static resetAppSettings() {
const resetAppSettingsMessage = 'By proceeding you will be removing all connected organizations and preferences from Zulip.';

1411
app/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "zulip",
"productName": "Zulip",
"version": "1.9.0",
"version": "2.3.1",
"description": "Zulip Desktop App",
"license": "Apache-2.0",
"copyright": "Kandra Labs, Inc.",
@@ -26,19 +26,21 @@
"InstantMessaging"
],
"dependencies": {
"auto-launch": "5.0.1",
"@electron-elements/send-feedback": "1.0.7",
"escape-html": "1.0.3",
"auto-launch": "5.0.5",
"electron-is-dev": "0.3.0",
"electron-log": "2.2.7",
"electron-log": "2.2.14",
"electron-spellchecker": "1.1.2",
"electron-updater": "2.21.4",
"electron-updater": "2.21.10",
"electron-window-state": "4.1.1",
"is-online": "7.0.0",
"node-json-db": "0.7.3",
"request": "2.81.0",
"request": "2.85.0",
"semver": "5.4.1",
"wurl": "2.5.0"
},
"optionalDependencies": {
"node-mac-notifier": "0.0.13"
"node-mac-notifier": "0.1.0"
}
}

View File

@@ -13,9 +13,6 @@ body {
#content {
display: flex;
height: 100%;
background: #fff url(../img/ic_loading.gif) no-repeat;
background-size: 60px 60px;
background-position: center;
}
.toggle-sidebar {
@@ -45,6 +42,28 @@ body {
transition: all 0.6s ease-out;
}
#view-controls-container {
height: calc(100% - 208px);
overflow-y: hidden;
}
#view-controls-container:hover {
overflow-y: overlay;
}
#view-controls-container::-webkit-scrollbar {
width: 4px;
}
#view-controls-container::-webkit-scrollbar-track {
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;
}
@font-face {
font-family: 'Material Icons';
font-style: normal;
@@ -251,6 +270,25 @@ body {
width: 100%;
}
/*Pseudo element for loading indicator*/
#webviews-container::before {
content: "";
position: absolute;
z-index: 1;
background: #fff url(../img/ic_loading.gif) no-repeat;
background-size: 60px 60px;
background-position: center;
width: 100%;
height: 100%;
}
/*When the active webview is loaded*/
#webviews-container.loaded::before {
opacity: 0;
z-index: -1;
visibility: hidden;
}
webview {
/* transition: opacity 0.3s ease-in; */
flex-grow: 1;
@@ -262,11 +300,6 @@ webview {
flex-direction: column;
}
webview.download-webview {
z-index: -1;
pointer-events: none;
}
webview.onload {
transition: opacity 1s cubic-bezier(0.95, 0.05, 0.795, 0.035);
}
@@ -287,6 +320,7 @@ webview.focus {
/* Tooltip styling */
#dnd-tooltip,
#back-tooltip,
#reload-tooltip,
#setting-tooltip {
@@ -304,6 +338,7 @@ webview.focus {
font-size: 14px;
}
#dnd-tooltip:after,
#back-tooltip:after,
#reload-tooltip:after,
#setting-tooltip:after {
@@ -408,3 +443,26 @@ webview.focus {
opacity: 1;
}
}
send-feedback {
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;
}
#feedback-modal.show {
display: flex;
}

View File

@@ -557,6 +557,22 @@ input.toggle-round:checked+label:after {
background: #329588;
}
.tip {
background-color: hsl(46,63%,95%);
border: 1px solid hsl(46,63%,84%);
border-radius: 4px;
}
.md-14 {
font-size: 14px;
vertical-align: middle;
padding-right: 6px;
}
#open-hotkeys-link {
text-decoration: underline;
cursor: pointer;
}
/* responsive grid */

View File

@@ -1,7 +1,6 @@
const { shell } = require('electron').remote;
const LinkUtil = require('../utils/link-util');
const DomainUtil = require('../utils/domain-util');
const hiddenWebView = require('../components/hidden-webview');
function handleExternalLink(event) {
const { url } = event;
@@ -16,12 +15,12 @@ function handleExternalLink(event) {
if (isWhiteListURL) {
event.preventDefault();
// only open the pdf, mp3, mp4 etc.. in hidden webview since opening the
// image in webview will do nothing and will not save it
// whereas the pdf will be saved to user desktop once openened in
// in the hidden webview and will not trigger webview reload
// 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
if (!LinkUtil.isImage(url) && isUploadsURL) {
hiddenWebView.loadURL(url);
this.$el.downloadURL(url);
return;
}

View File

@@ -1,9 +0,0 @@
// this hidden webview will be used to open pdf url and
// save it to user's computer without triggering a reload
// when navigating to pdf url to download it.
const hiddenWebView = document.createElement('webview');
hiddenWebView.classList.add('download-webview');
hiddenWebView.src = 'about:blank';
document.querySelector('#webviews-container').appendChild(hiddenWebView);
module.exports = hiddenWebView;

View File

@@ -18,9 +18,10 @@ class WebView extends BaseComponent {
this.props = props;
this.zoomFactor = 1.0;
this.loading = false;
this.loading = true;
this.badgeCount = 0;
this.customCSS = ConfigUtil.getConfigItem('customCSS');
this.$webviewsContainer = document.querySelector('#webviews-container').classList;
}
template() {
@@ -86,6 +87,7 @@ class WebView extends BaseComponent {
if (this.props.role === 'server') {
this.$el.classList.add('onload');
}
this.loading = false;
this.show();
});
@@ -119,6 +121,13 @@ class WebView extends BaseComponent {
return;
}
// To show or hide the loading indicator in the the active tab
if (this.loading) {
this.$webviewsContainer.remove('loaded');
} else {
this.$webviewsContainer.add('loaded');
}
this.$el.classList.remove('disabled');
this.$el.classList.add('active');
setTimeout(() => {
@@ -127,7 +136,6 @@ class WebView extends BaseComponent {
}
}, 1000);
this.focus();
this.loading = false;
this.props.onTitleChange();
// Injecting preload css in webview to override some css rules
this.$el.insertCSS(fs.readFileSync(path.join(__dirname, '/../../css/preload.css'), 'utf8'));
@@ -220,6 +228,9 @@ class WebView extends BaseComponent {
reload() {
this.hide();
// Shows the loading indicator till the webview is reloaded
this.$webviewsContainer.remove('loaded');
this.loading = true;
this.$el.reload();
}

View File

@@ -0,0 +1,62 @@
const { app } = require('electron').remote;
const path = require('path');
const fs = require('fs');
const SendFeedback = require('@electron-elements/send-feedback');
// make the button color match zulip app's theme
SendFeedback.customStyles = `
button:hover, button:focus {
border-color: #4EBFAC;
color: #4EBFAC;
}
button:active {
background-color: #f1f1f1;
color: #4EBFAC;
}
button {
background-color: #4EBFAC;
border-color: #4EBFAC;
}
`;
customElements.define('send-feedback', SendFeedback);
const sendFeedback = document.querySelector('send-feedback');
const feedbackHolder = sendFeedback.parentElement;
// customize the fields of custom elements
sendFeedback.title = 'Report Issue';
sendFeedback.titleLabel = 'Issue title:';
sendFeedback.titlePlaceholder = 'Enter issue title';
sendFeedback.textareaLabel = 'Describe the issue:';
sendFeedback.textareaPlaceholder = 'Succinctly describe your issue and steps to reproduce it...';
sendFeedback.buttonLabel = 'Report Issue';
sendFeedback.loaderSuccessText = '';
sendFeedback.useReporter('emailReporter', {
email: 'akash@zulipchat.com'
});
feedbackHolder.addEventListener('click', e => {
// only remove the class if the grey out faded
// part is clicked and not the feedback element itself
if (e.target === e.currentTarget) {
feedbackHolder.classList.remove('show');
}
});
sendFeedback.addEventListener('feedback-submitted', () => {
setTimeout(() => {
feedbackHolder.classList.remove('show');
}, 1000);
});
const dataDir = app.getPath('userData');
const logsDir = path.join(dataDir, '/Logs');
sendFeedback.logs.push(...fs.readdirSync(logsDir).map(file => path.join(logsDir, file)));
module.exports = {
feedbackHolder,
sendFeedback
};

View File

@@ -11,7 +11,9 @@ const WebView = require(__dirname + '/js/components/webview.js');
const ServerTab = require(__dirname + '/js/components/server-tab.js');
const FunctionalTab = require(__dirname + '/js/components/functional-tab.js');
const ConfigUtil = require(__dirname + '/js/utils/config-util.js');
const DNDUtil = require(__dirname + '/js/utils/dnd-util.js');
const ReconnectUtil = require(__dirname + '/js/utils/reconnect-util.js');
const { feedbackHolder } = require(__dirname + '/js/feedback.js');
class ServerManagerView {
constructor() {
@@ -23,12 +25,14 @@ class ServerManagerView {
this.$settingsButton = $actionsContainer.querySelector('#settings-action');
this.$webviewsContainer = document.getElementById('webviews-container');
this.$backButton = $actionsContainer.querySelector('#back-action');
this.$dndButton = $actionsContainer.querySelector('#dnd-action');
this.$addServerTooltip = document.getElementById('add-server-tooltip');
this.$reloadTooltip = $actionsContainer.querySelector('#reload-tooltip');
this.$settingsTooltip = $actionsContainer.querySelector('#setting-tooltip');
this.$serverIconTooltip = document.getElementsByClassName('server-tooltip');
this.$backTooltip = $actionsContainer.querySelector('#back-tooltip');
this.$dndTooltip = $actionsContainer.querySelector('#dnd-tooltip');
this.$sidebar = document.getElementById('sidebar');
@@ -85,9 +89,15 @@ class ServerManagerView {
startMinimized: false,
enableSpellchecker: true,
showNotification: true,
autoUpdate: true,
betaUpdate: false,
silent: false,
lastActiveTab: 0
lastActiveTab: 0,
dnd: false,
dndPreviousSettings: {
showNotification: true,
silent: false
}
};
// Platform specific settings
@@ -95,6 +105,7 @@ class ServerManagerView {
if (process.platform === 'win32') {
// Only available on Windows
settingOptions.flashTaskbarOnMessage = true;
settingOptions.dndPreviousSettings.flashTaskbarOnMessage = true;
}
for (const i in settingOptions) {
@@ -155,6 +166,11 @@ class ServerManagerView {
}
initActions() {
this.initDNDButton();
this.$dndButton.addEventListener('click', () => {
const dndUtil = DNDUtil.toggle();
ipcRenderer.send('forward-message', 'toggle-dnd', dndUtil.dnd, dndUtil.newSettings);
});
this.$reloadButton.addEventListener('click', () => {
this.tabs[this.activeTabIndex].webview.reload();
});
@@ -175,10 +191,16 @@ class ServerManagerView {
});
});
this.sidebarHoverEvent(this.$addServerButton, this.$addServerTooltip);
this.sidebarHoverEvent(this.$addServerButton, this.$addServerTooltip, true);
this.sidebarHoverEvent(this.$settingsButton, this.$settingsTooltip);
this.sidebarHoverEvent(this.$reloadButton, this.$reloadTooltip);
this.sidebarHoverEvent(this.$backButton, this.$backTooltip);
this.sidebarHoverEvent(this.$dndButton, this.$dndTooltip);
}
initDNDButton() {
const dnd = ConfigUtil.getConfigItem('dnd', false);
this.toggleDNDButton(dnd);
}
getTabIndex() {
@@ -187,9 +209,17 @@ class ServerManagerView {
return currentIndex;
}
sidebarHoverEvent(SidebarButton, SidebarTooltip) {
sidebarHoverEvent(SidebarButton, SidebarTooltip, addServer = false) {
SidebarButton.addEventListener('mouseover', () => {
SidebarTooltip.removeAttribute('style');
// To handle position of add server tooltip due to scrolling of list of organizations
// This could not be handled using CSS, hence the top of the tooltip is made same
// as that of its parent element.
// This needs to handled only for the add server tooltip and not others.
if (addServer) {
const { top } = SidebarButton.getBoundingClientRect();
SidebarTooltip.style.top = top + 'px';
}
});
SidebarButton.addEventListener('mouseout', () => {
SidebarTooltip.style.display = 'none';
@@ -197,8 +227,13 @@ class ServerManagerView {
}
onHover(index, serverName) {
this.$serverIconTooltip[index].innerHTML = serverName;
this.$serverIconTooltip[index].innerText = serverName;
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
// as that of its parent element.
const { top } = this.$serverIconTooltip[index].parentElement.getBoundingClientRect();
this.$serverIconTooltip[index].style.top = top + 'px';
}
onHoverOut(index) {
@@ -238,6 +273,9 @@ 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]);
}
@@ -324,6 +362,15 @@ class ServerManagerView {
}
});
});
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);
});
}
destroyTab(name, index) {
@@ -343,6 +390,9 @@ class ServerManagerView {
}
destroyView() {
// Show loading indicator
this.$webviewsContainer.classList.remove('loaded');
// Clear global variables
this.activeTabIndex = -1;
this.tabs = [];
@@ -390,6 +440,12 @@ class ServerManagerView {
}
}
// Toggles the dnd button icon.
toggleDNDButton(alert) {
this.$dndTooltip.textContent = (alert ? 'Turn Off' : 'Enable') + ' Do Not Disturb';
this.$dndButton.querySelector('i').textContent = alert ? 'notifications_off' : 'notifications';
}
registerIpcs() {
const webviewListeners = {
'webview-reload': 'reload',
@@ -496,6 +552,10 @@ class ServerManagerView {
}
ipcRenderer.send('update-taskbar-icon', createOverlayIcon(messageCount).toDataURL(), String(messageCount));
});
ipcRenderer.on('open-feedback-modal', () => {
feedbackHolder.classList.add('show');
});
}
}

View File

@@ -48,7 +48,11 @@ class GeneralSection extends BaseSection {
</div>
</div>
<div class="title">App Updates</div>
<div class="settings-card">
<div class="settings-card">
<div class="setting-row" id="autoupdate-option">
<div class="setting-description">Enable auto updates</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="betaupdate-option">
<div class="setting-description">Get beta updates</div>
<div class="setting-control"></div>
@@ -104,7 +108,8 @@ class GeneralSection extends BaseSection {
this.updateTrayOption();
this.updateBadgeOption();
this.updateSilentOption();
this.updateUpdateOption();
this.autoUpdateOption();
this.betaUpdateOption();
this.updateSidebarOption();
this.updateStartAtLoginOption();
this.updateResetDataOption();
@@ -160,14 +165,26 @@ class GeneralSection extends BaseSection {
});
}
updateUpdateOption() {
autoUpdateOption() {
this.generateSettingOption({
$element: document.querySelector('#autoupdate-option .setting-control'),
value: ConfigUtil.getConfigItem('autoUpdate', true),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('autoUpdate');
ConfigUtil.setConfigItem('autoUpdate', newValue);
this.autoUpdateOption();
}
});
}
betaUpdateOption() {
this.generateSettingOption({
$element: document.querySelector('#betaupdate-option .setting-control'),
value: ConfigUtil.getConfigItem('betaUpdate', false),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('betaUpdate');
ConfigUtil.setConfigItem('betaUpdate', newValue);
this.updateUpdateOption();
this.betaUpdateOption();
}
});
}

View File

@@ -97,6 +97,15 @@ class PreferenceView extends BaseComponent {
ipcRenderer.on('toggletray', (event, state) => {
this.handleToggle('tray-option', state);
});
ipcRenderer.on('toggle-dnd', (event, state, newSettings) => {
this.handleToggle('show-notification-option', newSettings.showNotification);
this.handleToggle('silent-option', newSettings.silent);
if (process.platform === 'win32') {
this.handleToggle('flash-taskbar-option', newSettings.flashTaskbarOnMessage);
}
});
}
}

View File

@@ -1,6 +1,7 @@
'use strict';
const BaseSection = require(__dirname + '/base-section.js');
const shell = require('electron').shell;
class ShortcutsSection extends BaseSection {
constructor(props) {
@@ -23,6 +24,10 @@ class ShortcutsSection extends BaseSection {
<tr>
<td><kbd>${userOSKey}</kbd><kbd>K</kbd></td>
<td>Keyboard Shortcuts</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>Shift</kbd> + <kbd>M</kbd></td>
<td>Toggle Do Not Disturb</td>
</tr>
<tr>
<td><kbd>Shift</kbd><kbd>${userOSKey}</kbd><kbd>D</kbd></td>
@@ -155,6 +160,7 @@ 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>
`;
}
@@ -174,6 +180,10 @@ class ShortcutsSection extends BaseSection {
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>K</kbd></td>
<td>Keyboard Shortcuts</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>Shift</kbd> + <kbd>M</kbd></td>
<td>Toggle Do Not Disturb</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>L</kbd></td>
@@ -290,13 +300,22 @@ 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>
`;
}
openHotkeysExternalLink() {
const link = 'https://zulipchat.com/help/keyboard-shortcuts';
const externalCreateNewOrgEl = document.getElementById('open-hotkeys-link');
externalCreateNewOrgEl.addEventListener('click', () => {
shell.openExternal(link);
});
}
init() {
this.props.$root.innerHTML = (process.platform === 'darwin') ?
this.templateMac() : this.templateWinLin();
this.openHotkeysExternalLink();
}
}

View File

@@ -98,7 +98,7 @@ const renderNativeImage = function (arg) {
return Promise.resolve()
.then(() => renderCanvas(arg))
.then(canvas => {
const pngData = nativeImage.createFromDataURL(canvas.toDataURL('image/png')).toPng();
const pngData = nativeImage.createFromDataURL(canvas.toDataURL('image/png')).toPNG();
return Promise.resolve(nativeImage.createFromBuffer(pngData, config.pixelRatio));
});
};

View File

@@ -0,0 +1,41 @@
'use strict';
const ConfigUtil = require(__dirname + '/config-util.js');
function toggle() {
const dnd = !ConfigUtil.getConfigItem('dnd', false);
const dndSettingList = ['showNotification', 'silent'];
if (process.platform === 'win32') {
dndSettingList.push('flashTaskbarOnMessage');
}
let newSettings;
if (dnd) {
const oldSettings = {};
newSettings = {};
// Iterate through the dndSettingList.
for (const settingName of dndSettingList) {
// Store the current value of setting.
oldSettings[settingName] = ConfigUtil.getConfigItem(settingName);
// New value of setting.
newSettings[settingName] = (settingName === 'silent');
}
// Store old value in oldSettings.
ConfigUtil.setConfigItem('dndPreviousSettings', oldSettings);
} else {
newSettings = ConfigUtil.getConfigItem('dndPreviousSettings');
}
for (const settingName of dndSettingList) {
ConfigUtil.setConfigItem(settingName, newSettings[settingName]);
}
ConfigUtil.setConfigItem('dnd', dnd);
return {dnd, newSettings};
}
module.exports = {
toggle
};

View File

@@ -5,6 +5,8 @@ const fs = require('fs');
const path = require('path');
const JsonDB = require('node-json-db');
const request = require('request');
const escape = require('escape-html');
const Logger = require('./logger-util');
const logger = new Logger({
@@ -188,7 +190,7 @@ 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: data.realm_name
alias: escape(data.realm_name)
});
}
} else {

View File

@@ -13,12 +13,12 @@ class ReconnectUtil {
pollInternetAndReload() {
const pollInterval = setInterval(() => {
this._checkAndReload()
.then(status => {
if (status) {
this.alreadyReloaded = true;
clearInterval(pollInterval);
}
});
.then(status => {
if (status) {
this.alreadyReloaded = true;
clearInterval(pollInterval);
}
});
}, 1500);
}
@@ -26,22 +26,24 @@ class ReconnectUtil {
return new Promise(resolve => {
if (!this.alreadyReloaded) { // eslint-disable-line no-negated-condition
isOnline()
.then(online => {
if (online) {
if (!this.alreadyReloaded) {
this.serverManagerView.reloadView();
.then(online => {
if (online) {
if (!this.alreadyReloaded) {
this.serverManagerView.reloadView();
}
console.log('You\'re back online.');
return resolve(true);
}
console.log('You\'re back online.');
return resolve(true);
}
console.log('There is no internet connection, try checking network cables, modem and router.');
const errMsgHolder = document.querySelector('#description');
errMsgHolder.innerHTML = `
console.log('There is no internet connection, try checking network cables, modem and router.');
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>`;
return resolve(false);
});
}
return resolve(false);
});
} else {
return resolve(true);
}

View File

@@ -24,6 +24,10 @@
</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>
@@ -42,7 +46,11 @@
<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>
</html>
</html>

View File

@@ -6,9 +6,9 @@ os: Previous Visual Studio 2015
cache:
- node_modules
install:
- ps: Install-Product node 6 x64
- ps: Install-Product node 8 x64
- git reset --hard HEAD
- npm install npm -g
- node --version
@@ -21,4 +21,4 @@ build: off
test_script:
- npm run test
- npm run test-e2e
# - npm run test-e2e

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 12 KiB

10034
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "zulip",
"productName": "Zulip",
"version": "1.9.0",
"version": "2.3.1",
"main": "./app/main",
"description": "Zulip Desktop App",
"license": "Apache-2.0",
@@ -46,7 +46,8 @@
],
"copyright": "©2017 Kandra Labs, Inc.",
"mac": {
"category": "public.app-category.productivity"
"category": "public.app-category.productivity",
"artifactName": "${productName}-${version}-${arch}.${ext}"
},
"linux": {
"category": "Chat;GNOME;GTK;Network;InstantMessaging",
@@ -58,7 +59,8 @@
"AppImage",
"snap"
],
"maintainer": "Akash Nimare <svnitakash@gmail.com>"
"maintainer": "Akash Nimare <svnitakash@gmail.com>",
"artifactName": "${productName}-${version}-${arch}.${ext}"
},
"deb": {
"synopsis": "Zulip Desktop App",
@@ -71,20 +73,24 @@
"dmg": {
"background": "build/appdmg.png",
"icon": "build/icon.icns",
"iconSize": 128,
"iconSize": 100,
"contents": [
{
"x": 380,
"y": 240,
"y": 280,
"type": "link",
"path": "/Applications"
},
{
"x": 122,
"y": 240,
"x": 110,
"y": 280,
"type": "file"
}
]
],
"window": {
"width": 500,
"height": 500
}
},
"win": {
"target": [
@@ -117,12 +123,12 @@
"assert": "1.4.1",
"cp-file": "^5.0.0",
"devtron": "1.4.0",
"electron": "1.8.4",
"electron-builder": "20.8.1",
"electron": "2.0.1",
"electron-builder": "20.13.4",
"electron-connect": "0.6.2",
"electron-debug": "1.4.0",
"google-translate-api": "2.3.0",
"gulp": "3.9.1",
"gulp": "^4.0.0",
"gulp-tape": "0.0.9",
"is-ci": "^1.0.10",
"nodemon": "^1.14.11",

View File

@@ -15,6 +15,6 @@ fi
npm run test
if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
npm run test-e2e
fi
# if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
# npm run test-e2e
# fi

BIN
snap/gui/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -1,10 +1,10 @@
name: zulip
version: 1.8.2
summary: Zulip
description: Zulip Desktop Client for Linux
version: 2.0.0
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
grade: stable
icon: ../build/icon.png
icon: snap/gui/icon.png
apps:
zulip:
command: env TMPDIR=$XDG_RUNTIME_DIR desktop-launch $SNAP/zulip
@@ -32,6 +32,6 @@ parts:
- libpulse0
- libxss1
- libxtst6
source: ../dist/linux-unpacked
source: dist/linux-unpacked
after:
- desktop-gtk2