Compare commits

...

9 Commits

Author SHA1 Message Date
Akash Nimare
4e21ff9d6c menu: Update the method to get current active server. 2019-06-20 22:11:52 +05:30
Akash Nimare
037cb227da menu: Open help page of current active server.
Fixes: #758.
2019-06-19 03:41:29 +05:30
Kanishk Kakar
88b764dcc9 sidebar: Add loading indicator.
* Browser-like loading indicator added to the sidebar.
Shows when the app is loading a page. Inactive once the user starts
navigating on the webpage, and in settings.

* Add tooltip saying "Loading" to indicator.

Fixes #430.
2019-06-15 16:03:50 +05:30
Kanishk Kakar
9fe72c0d21 mennu: Add option to hide menu bar to View menu.
* Add View menu shortcut to hide menu bar
The hide menu bar setting available in General Preferences has been added to the View menu.

* settings: Add method to coordinate menu & settings
Use autoHideMenuBar setting with said method.
2019-06-15 04:29:06 +05:30
Kanishk Kakar
31b00ee6a6 Update server validation logic.
This PR removes .ogg file check (supported only by very old servers). Other enhancements in server validation logic -
* Reject domains with no organizations. 
* Convert validation methods to async await
* Add messages.js for returning error message strings.

Fixes: #596, #573.
2019-06-13 00:26:32 +05:30
ViPuL
ecec7f8b89 docs: Improve development guide.
Minifies the current development guide by
a significant amount. Also, removes system requirements
as no credible source for them. Removes dependency
specific instructions and adds links to instructions
to install them.

Fixes #341.
2019-06-11 22:44:06 +05:30
Kanishk Kakar
139496b716 settings: Add field to find accounts by email.
Takes in an organization URL for taking the user to /accounts/find the page.
2019-06-04 21:29:33 +05:30
Akash Nimare
f6239132c1 electron: Update electron to v3.1.10. 2019-05-30 00:59:01 +05:30
Kanishk Kakar
6451373ff6 changelog: Add release notes for v3.0.0. 2019-05-22 01:20:21 +05:30
17 changed files with 451 additions and 203 deletions

View File

@@ -254,6 +254,7 @@ app.on('ready', () => {
ipcMain.on('toggle-menubar', (event, showMenubar) => {
mainWindow.setAutoHideMenuBar(showMenubar);
mainWindow.setMenuBarVisibility(!showMenubar);
page.send('toggle-autohide-menubar', showMenubar, true);
});
ipcMain.on('update-badge', (event, messageCount) => {

View File

@@ -162,6 +162,20 @@ class AppMenu {
ConfigUtil.setConfigItem('showSidebar', newValue);
}
}
}, {
label: 'Auto hide Menu bar',
checked: ConfigUtil.getConfigItem('autoHideMenubar', false),
visible: process.platform !== 'darwin',
click(item, focusedWindow) {
if (focusedWindow) {
const newValue = !ConfigUtil.getConfigItem('autoHideMenubar');
focusedWindow.setAutoHideMenuBar(newValue);
focusedWindow.setMenuBarVisibility(!newValue);
focusedWindow.webContents.send('toggle-autohide-menubar', newValue);
ConfigUtil.setConfigItem('autoHideMenubar', newValue);
}
},
type: 'checkbox'
}];
}
@@ -181,8 +195,10 @@ class AppMenu {
},
{
label: `Help Center`,
click() {
shell.openExternal('https://zulipchat.com/help/');
click(focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('open-help');
}
}
}, {
label: 'Report an Issue',

2
app/package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "zulip",
"version": "2.5.0-beta",
"version": "3.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -267,6 +267,18 @@ body {
margin-bottom: 5px;
}
.refresh {
animation: rotate-loader 1s linear infinite;
}
@keyframes rotate-loader {
from {
transform: rotate(0);
}
to {
transform: rotate(-360deg);
}
}
/*******************
* Webview Area *
@@ -328,6 +340,7 @@ webview.focus {
/* Tooltip styling */
#loading-tooltip,
#dnd-tooltip,
#back-tooltip,
#reload-tooltip,
@@ -346,6 +359,7 @@ webview.focus {
font-size: 14px;
}
#loading-tooltip::after,
#dnd-tooltip::after,
#back-tooltip::after,
#reload-tooltip::after,

View File

@@ -259,6 +259,10 @@ img.server-info-icon {
border: rgba(78, 191, 172, 1.000) 2px solid;
}
.invalid-input-value:focus {
border: rgba(239, 83, 80, 1) 2px solid;
}
.manual-proxy-block {
width: 96%;
}

View File

@@ -94,6 +94,7 @@ class WebView extends BaseComponent {
this.$el.classList.add('onload');
}
this.loading = false;
this.props.switchLoading(false, this.props.url);
this.show();
// Refocus text boxes after reload
@@ -112,6 +113,10 @@ class WebView extends BaseComponent {
});
this.$el.addEventListener('did-start-loading', () => {
const isSettingPage = this.props.url.includes('renderer/preference.html');
if (!isSettingPage) {
this.props.switchLoading(true, this.props.url);
}
let userAgent = SystemUtil.getUserAgent();
if (!userAgent) {
SystemUtil.setUserAgent(this.$el.getUserAgent());
@@ -119,6 +124,10 @@ class WebView extends BaseComponent {
}
this.$el.setUserAgent(userAgent);
});
this.$el.addEventListener('did-stop-loading', () => {
this.props.switchLoading(false, this.props.url);
});
}
getBadgeCount(title) {

View File

@@ -1,6 +1,6 @@
'use strict';
const { ipcRenderer, remote, clipboard } = require('electron');
const { ipcRenderer, remote, clipboard, shell } = require('electron');
const isDev = require('electron-is-dev');
const { session, app, Menu, dialog } = remote;
@@ -31,6 +31,7 @@ class ServerManagerView {
const $actionsContainer = document.getElementById('actions-container');
this.$reloadButton = $actionsContainer.querySelector('#reload-action');
this.$loadingIndicator = $actionsContainer.querySelector('#loading-action');
this.$settingsButton = $actionsContainer.querySelector('#settings-action');
this.$webviewsContainer = document.getElementById('webviews-container');
this.$backButton = $actionsContainer.querySelector('#back-action');
@@ -38,6 +39,7 @@ class ServerManagerView {
this.$addServerTooltip = document.getElementById('add-server-tooltip');
this.$reloadTooltip = $actionsContainer.querySelector('#reload-tooltip');
this.$loadingTooltip = $actionsContainer.querySelector('#loading-tooltip');
this.$settingsTooltip = $actionsContainer.querySelector('#setting-tooltip');
this.$serverIconTooltip = document.getElementsByClassName('server-tooltip');
this.$backTooltip = $actionsContainer.querySelector('#back-tooltip');
@@ -49,6 +51,7 @@ class ServerManagerView {
this.$fullscreenEscapeKey = process.platform === 'darwin' ? '^⌘F' : 'F11';
this.$fullscreenPopup.innerHTML = `Press ${this.$fullscreenEscapeKey} to exit full screen`;
this.loading = {};
this.activeTabIndex = -1;
this.tabs = [];
this.functionalTabs = {};
@@ -100,6 +103,7 @@ class ServerManagerView {
initDefaultSettings() {
// Default settings which should be respected
const settingOptions = {
autoHideMenubar: false,
trayIcon: true,
useManualProxy: false,
useSystemProxy: false,
@@ -192,12 +196,21 @@ class ServerManagerView {
isActive: () => {
return index === this.activeTabIndex;
},
switchLoading: (loading, url) => {
if (!loading && this.loading[url]) {
this.loading[url] = false;
} else if (loading && !this.loading[url]) {
this.loading[url] = true;
}
this.showLoading(this.loading[this.tabs[this.activeTabIndex].webview.props.url]);
},
onNetworkError: this.openNetworkTroubleshooting.bind(this),
onTitleChange: this.updateBadge.bind(this),
nodeIntegration: false,
preload: true
})
}));
this.loading[server.url] = true;
}
initActions() {
@@ -238,6 +251,7 @@ class ServerManagerView {
});
this.sidebarHoverEvent(this.$addServerButton, this.$addServerTooltip, true);
this.sidebarHoverEvent(this.$loadingIndicator, this.$loadingTooltip);
this.sidebarHoverEvent(this.$settingsButton, this.$settingsTooltip);
this.sidebarHoverEvent(this.$reloadButton, this.$reloadTooltip);
this.sidebarHoverEvent(this.$backButton, this.$backTooltip);
@@ -255,6 +269,10 @@ class ServerManagerView {
return currentIndex;
}
getCurrentActiveServer() {
return this.tabs[this.activeTabIndex].webview.props.url;
}
displayInitialCharLogo($img, index) {
/*
index parameter needed because webview[data-tab-id] can increment
@@ -344,6 +362,14 @@ class ServerManagerView {
isActive: () => {
return this.functionalTabs[tabProps.name] === this.activeTabIndex;
},
switchLoading: (loading, url) => {
if (!loading && this.loading[url]) {
this.loading[url] = false;
} else if (loading && !this.loading[url]) {
this.loading[url] = true;
}
this.showLoading(this.loading[this.tabs[this.activeTabIndex].webview.props.url]);
},
onNetworkError: this.openNetworkTroubleshooting.bind(this),
onTitleChange: this.updateBadge.bind(this),
nodeIntegration: true,
@@ -435,6 +461,8 @@ class ServerManagerView {
this.activeTabIndex = index;
this.tabs[index].activate();
this.showLoading(this.loading[this.tabs[this.activeTabIndex].webview.props.url]);
ipcRenderer.send('update-menu', {
// JSON stringify this.tabs to avoid a crash
// util.inspect is being used to handle circular references
@@ -445,6 +473,16 @@ class ServerManagerView {
});
}
showLoading(loading) {
if (!loading) {
this.$reloadButton.removeAttribute('style');
this.$loadingIndicator.style.display = 'none';
} else if (loading) {
this.$reloadButton.style.display = 'none';
this.$loadingIndicator.removeAttribute('style');
}
}
destroyTab(name, index) {
if (this.tabs[index].webview.loading) {
return;
@@ -505,6 +543,15 @@ class ServerManagerView {
ipcRenderer.send('update-badge', messageCountAll);
}
updateGeneralSettings(setting, value) {
const selector = 'webview:not([class*=disabled])';
const webview = document.querySelector(selector);
if (webview) {
const webContents = webview.getWebContents();
webContents.send(setting, value);
}
}
toggleSidebar(show) {
if (show) {
this.$sidebar.classList.remove('sidebar-hide');
@@ -580,6 +627,12 @@ class ServerManagerView {
ipcRenderer.on('open-about', this.openAbout.bind(this));
ipcRenderer.on('open-help', () => {
// Open help page of current active server
const helpPage = this.getCurrentActiveServer() + '/help';
shell.openExternal(helpPage);
});
ipcRenderer.on('reload-viewer', this.reloadView.bind(this, this.tabs[this.activeTabIndex].props.index));
ipcRenderer.on('reload-current-viewer', this.reloadCurrentView.bind(this));
@@ -614,10 +667,7 @@ class ServerManagerView {
this.toggleSidebar(show);
// Toggle sidebar switch in the general settings
const selector = 'webview:not([class*=disabled])';
const webview = document.querySelector(selector);
const webContents = webview.getWebContents();
webContents.send('toggle-sidebar-setting', show);
this.updateGeneralSettings('toggle-sidebar-setting', show);
});
ipcRenderer.on('toggle-silent', (event, state) => {
@@ -634,6 +684,17 @@ class ServerManagerView {
});
});
ipcRenderer.on('toggle-autohide-menubar', (event, autoHideMenubar, updateMenu) => {
if (updateMenu) {
ipcRenderer.send('update-menu', {
tabs: this.tabsForIpc,
activeTabIndex: this.activeTabIndex
});
return;
}
this.updateGeneralSettings('toggle-menubar-setting', autoHideMenubar);
});
ipcRenderer.on('toggle-dnd', (event, state, newSettings) => {
this.toggleDNDButton(state);
ipcRenderer.send('forward-message', 'toggle-silent', newSettings.silent);
@@ -735,7 +796,7 @@ class ServerManagerView {
});
ipcRenderer.on('copy-zulip-url', () => {
clipboard.writeText(DomainUtil.getDomain(this.activeTabIndex).url);
clipboard.writeText(this.getCurrentActiveServer());
});
ipcRenderer.on('new-server', () => {

View File

@@ -4,6 +4,7 @@ const BaseSection = require(__dirname + '/base-section.js');
const DomainUtil = require(__dirname + '/../../utils/domain-util.js');
const ServerInfoForm = require(__dirname + '/server-info-form.js');
const AddCertificate = require(__dirname + '/add-certificate.js');
const FindAccounts = require(__dirname + '/find-accounts.js');
class ConnectedOrgSection extends BaseSection {
constructor(props) {
@@ -20,6 +21,8 @@ class ConnectedOrgSection extends BaseSection {
<div id="new-org-button"><button class="green sea w-250">Connect to another organization</button></div>
<div class="page-title">Add Custom Certificates</div>
<div id="add-certificate-container"></div>
<div class="page-title">Find accounts by email</div>
<div id="find-accounts-container"></div>
</div>
`;
}
@@ -38,6 +41,7 @@ class ConnectedOrgSection extends BaseSection {
this.$existingServers = document.getElementById('existing-servers');
this.$newOrgButton = document.getElementById('new-org-button');
this.$addCertificateContainer = document.getElementById('add-certificate-container');
this.$findAccountsContainer = document.getElementById('find-accounts-container');
const noServerText = 'All the connected orgnizations will appear here';
// Show noServerText if no servers are there otherwise hide it
@@ -59,6 +63,7 @@ class ConnectedOrgSection extends BaseSection {
});
this.initAddCertificate();
this.initFindAccounts();
}
initAddCertificate() {
@@ -67,6 +72,11 @@ class ConnectedOrgSection extends BaseSection {
}).init();
}
initFindAccounts() {
new FindAccounts({
$root: this.$findAccountsContainer
}).init();
}
}
module.exports = ConnectedOrgSection;

View File

@@ -0,0 +1,72 @@
'use-strict';
const { shell } = require('electron');
const BaseComponent = require(__dirname + '/../../components/base.js');
class FindAccounts extends BaseComponent {
constructor(props) {
super();
this.props = props;
}
template() {
return `
<div class="settings-card certificate-card">
<div class="certificate-input">
<div>Organization URL</div>
<input class="setting-input-value" value="zulipchat.com"/>
</div>
<div class="certificate-input">
<button class="green w-150" id="find-accounts-button">Find accounts</button>
</div>
</div>
`;
}
init() {
this.$findAccounts = this.generateNodeFromTemplate(this.template());
this.props.$root.appendChild(this.$findAccounts);
this.$findAccountsButton = this.$findAccounts.querySelector('#find-accounts-button');
this.$serverUrlField = this.$findAccounts.querySelectorAll('input.setting-input-value')[0];
this.initListeners();
}
findAccounts(url) {
if (!url) {
return;
}
if (!url.startsWith('http')) {
url = 'https://' + url;
}
shell.openExternal(url + '/accounts/find');
}
initListeners() {
this.$findAccountsButton.addEventListener('click', () => {
this.findAccounts(this.$serverUrlField.value);
});
this.$serverUrlField.addEventListener('click', () => {
if (this.$serverUrlField.value === 'zulipchat.com') {
this.$serverUrlField.setSelectionRange(0, 0);
}
});
this.$serverUrlField.addEventListener('keypress', event => {
if (event.keyCode === 13) {
this.findAccounts(this.$serverUrlField.value);
}
});
this.$serverUrlField.addEventListener('input', () => {
if (this.$serverUrlField.value) {
this.$serverUrlField.classList.remove('invalid-input-value');
} else {
this.$serverUrlField.classList.add('invalid-input-value');
}
});
}
}
module.exports = FindAccounts;

View File

@@ -94,6 +94,10 @@ class PreferenceView extends BaseComponent {
this.handleToggle('sidebar-option', state);
});
ipcRenderer.on('toggle-menubar-setting', (event, state) => {
this.handleToggle('menubar-option', state);
});
ipcRenderer.on('toggletray', (event, state) => {
this.handleToggle('tray-option', state);
});

View File

@@ -10,6 +10,7 @@ const escape = require('escape-html');
const Logger = require('./logger-util');
const RequestUtil = require(__dirname + '/../utils/request-util.js');
const Messages = require(__dirname + '/../../../resources/messages.js');
const logger = new Logger({
file: `domain-util.log`,
@@ -101,19 +102,50 @@ class DomainUtil {
return false;
}
async checkCertError(domain, serverConf, error, silent) {
if (silent) {
// since getting server settings has already failed
return serverConf;
} else {
// Report error to sentry to get idea of possible certificate errors
// users get when adding the servers
logger.reportSentry(new Error(error));
const certErrorMessage = Messages.certErrorMessage(domain, error);
const certErrorDetail = Messages.certErrorDetail();
const response = await dialog.showMessageBox({
type: 'warning',
buttons: ['Yes', 'No'],
defaultId: 1,
message: certErrorMessage,
detail: certErrorDetail
});
if (response === 0) {
// set ignoreCerts parameter to true in case user responds with yes
serverConf.ignoreCerts = true;
try {
return await this.getServerSettings(domain, serverConf.ignoreCerts);
} catch (err) {
if (error === Messages.noOrgsError(domain)) {
throw new Error(error);
}
return serverConf;
}
} else {
throw new Error('Untrusted certificate.');
}
}
}
// ignoreCerts parameter helps in fetching server icon and
// other server details when user chooses to ignore certificate warnings
checkDomain(domain, ignoreCerts = false, silent = false) {
async checkDomain(domain, ignoreCerts = false, silent = false) {
if (!silent && this.duplicateDomain(domain)) {
// Do not check duplicate in silent mode
return Promise.reject('This server has been added.');
throw new Error('This server has been added.');
}
domain = this.formatUrl(domain);
const checkDomain = {
url: domain + '/static/audio/zulip.ogg',
...RequestUtil.requestOptions(domain, ignoreCerts)
};
const serverConf = {
icon: defaultIconUrl,
@@ -122,70 +154,29 @@ class DomainUtil {
ignoreCerts
};
return new Promise((resolve, reject) => {
request(checkDomain, (error, response) => {
// If the domain contains following strings we just bypass the server
const whitelistDomains = [
'zulipdev.org'
];
try {
return await this.getServerSettings(domain, serverConf.ignoreCerts);
} catch (err) {
// If the domain contains following strings we just bypass the server
const whitelistDomains = [
'zulipdev.org'
];
// make sure that error is an error or string not undefined
// so validation does not throw error.
error = error || '';
// make sure that error is an error or string not undefined
// so validation does not throw error.
const error = err || '';
const certsError = error.toString().includes('certificate');
if (!error && response.statusCode < 400) {
// Correct
this.getServerSettings(domain, serverConf.ignoreCerts).then(serverSettings => {
resolve(serverSettings);
}, () => {
resolve(serverConf);
});
} else if (domain.indexOf(whitelistDomains) >= 0 || certsError) {
if (silent) {
this.getServerSettings(domain, serverConf.ignoreCerts).then(serverSettings => {
resolve(serverSettings);
}, () => {
resolve(serverConf);
});
} else {
// Report error to sentry to get idea of possible certificate errors
// users get when adding the servers
logger.reportSentry(new Error(error));
const certErrorMessage = `Do you trust certificate from ${domain}? \n ${error}`;
const certErrorDetail = `The organization you're connecting to is either someone impersonating the Zulip server you entered, or the server you're trying to connect to is configured in an insecure way.
\nIf you have a valid certificate please add it from Settings>Organizations and try to add the organization again.
\nUnless you have a good reason to believe otherwise, you should not proceed.
\nYou can click here if you'd like to proceed with the connection.`;
dialog.showMessageBox({
type: 'warning',
buttons: ['Yes', 'No'],
defaultId: 1,
message: certErrorMessage,
detail: certErrorDetail
}, response => {
if (response === 0) {
// set ignoreCerts parameter to true in case user responds with yes
serverConf.ignoreCerts = true;
this.getServerSettings(domain, serverConf.ignoreCerts).then(serverSettings => {
resolve(serverSettings);
}, () => {
resolve(serverConf);
});
} else {
reject('Untrusted Certificate.');
}
});
}
} else {
const invalidZulipServerError = `${domain} does not appear to be a valid Zulip server. Make sure that \
\n (1) you can connect to that URL in a web browser and \n (2) if you need a proxy to connect to the Internet, that you've configured your proxy in the Network settings \n (3) its a zulip server \
\n (4) the server has a valid certificate, you can add custom certificates in Settings>Organizations`;
reject(invalidZulipServerError);
const certsError = error.toString().includes('certificate');
if (domain.indexOf(whitelistDomains) >= 0 || certsError) {
try {
return await this.checkCertError(domain, serverConf, error, silent);
} catch (err) {
throw err;
}
});
});
} else {
throw Messages.invalidZulipServerError(domain);
}
}
}
getServerSettings(domain, ignoreCerts = false) {
@@ -207,9 +198,11 @@ class DomainUtil {
alias: escape(data.realm_name),
ignoreCerts
});
} else {
reject(Messages.noOrgsError(domain));
}
} else {
reject('Zulip server version < 1.6.');
reject(response);
}
});
});

View File

@@ -32,6 +32,10 @@
<i class="material-icons md-48">refresh</i>
<span id="reload-tooltip" style="display:none">Reload</span>
</div>
<div class="action-button" id="loading-action">
<i class="refresh material-icons md-48">loop</i>
<span id="loading-tooltip" style="display:none">Loading</span>
</div>
<div class="action-button disable" id="back-action">
<i class="material-icons md-48">arrow_back</i>
<span id="back-tooltip" style="display:none">Go Back</span>
@@ -53,4 +57,4 @@
</body>
<script src="js/main.js"></script>
<script>require('./js/shared/preventdrag.js')</script>
</html>
</html>

27
app/resources/messages.js Normal file
View File

@@ -0,0 +1,27 @@
class Messages {
invalidZulipServerError(domain) {
return `${domain} does not appear to be a valid Zulip server. Make sure that
\n • You can connect to that URL in a web browser.\
\n • If you need a proxy to connect to the Internet, that you've configured your proxy in the Network settings.\
\n • It's a Zulip server. (The oldest supported version is 1.6).\
\n • The server has a valid certificate. (You can add custom certificates in Settings > Organizations).`;
}
noOrgsError(domain) {
return `${domain} does not have any organizations added.\
\nPlease contact your server administrator.`;
}
certErrorMessage(domain, error) {
return `Do you trust certificate from ${domain}? \n ${error}`;
}
certErrorDetail() {
return `The organization you're connecting to is either someone impersonating the Zulip server you entered, or the server you're trying to connect to is configured in an insecure way.
\nIf you have a valid certificate please add it from Settings>Organizations and try to add the organization again.
\nUnless you have a good reason to believe otherwise, you should not proceed.
\nYou can click here if you'd like to proceed with the connection.`;
}
}
module.exports = new Messages();

View File

@@ -2,6 +2,77 @@
All notable changes to the Zulip desktop app are documented in this file.
### v3.0.0 --2019-05-20
**New features**:
* Add context menu in left sidebar.
* Enable per-user installation on Windows.
* Switch to next server on Ctrl+Tab.
* Add option to copy zulip URL.
* Allow zoom options from numpad.
* Use server language for spellchecker for all platforms.
* Allow installing app without admin privileges.
* Allow insecure requests on user request.
* Unify case across menus and settings.
**Enhancements**:
* Remove Found bug button.
* Set custom css to false by default.
* Disable beta updates if auto updates disabled
* Update menu items on setting page.
* Include certificates in all requests for icon.
* Document show sidebar shortcut properly.
* Improve organization page.
* Improve wording of adding a new org button.
* Increase width of add a new org button.
* Add eol for linebreaks on windows.
* Teach git to ignore unnecessary binary files.
* Send user-agent with request.
* Minimize to tray on startup.
* Update test config files.
* Ensure backward compatibility when using narrow.by_topic.
* Use path.sep for path separator to support Windows.
* Change the window title to contain active Realm's name.
* Use path.basename to get certificate file name.
* Disable pdf-viewer window.
* Default to starting app on login.
* Modify reset app data button.
* Add requestOptions to replace request instances.
* Workaround buggy focus switching in Electron 3.0.10.
* Reorder file menu and add option to Add Organization.
* Improve development guide.
* Implement CSS linting with stylelint.
* Add "role" key to webview property.
* Implement HTML Linting with htmlhint and fix indent.
* Limit the number of lines in log files.
* Fix focus after clicking back button.
* Remove minimize and close from File menu.
* Add config for installer name.
**Fixes**:
* Fix `request` ecdhCurve mismatch errors
* Fix typo in network error message.
* Fix context menu not working on adding new org.
* Fix reply from notification.
* Fix shorcut section horizontal alignment.
* Fix broken link in docs.
* Fix grammatical errors.
* Fix typo error in issue template.
* Fix text for Toggle DND in sidebar on hover.
* Fix focus after soft reload.
* Fix tip's place for Windows & Linux.
**Module updates**:
* Update node-json-db to v0.9.1.
* Update sentry to v0.12.1.
* Update electron-window-state to v5.0.3.
* Update electron to v3.0.10.
* Update electron-builder to v20.40.2.
* Update electron-sentry to v0.14.0.
* Update dependencies to fix minor dev security alerts.
* Update snap config.
### v2.3.82 --2018-09-25
**New features**:

View File

@@ -1,152 +1,114 @@
# Improve development guide
# Development Setup
# Development setup
This is a guide to running the Zulip desktop app from source,
in order to contribute to developing it.
This is a guide to running the Zulip desktop app from a source tree, in order to contribute to developing it. The Zulip electron development environment can be installed on **macOS, Windows, and Linux** (Debian or Ubuntu recommended). Youll need at least **2GB of available RAM**. Installing the Zulip electron development environment requires downloading several hundred megabytes of dependencies, so you will need an **active, reasonably fast, internet connection throughout the entire installation processes.**
## Prerequisites
# Set up Git & GitHub
To build and run the app from source, you'll need the following:
You can skip this step if you already have Git, GitHub.
* [Git](http://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* Use our [Git Guide](https://zulip.readthedocs.io/en/latest/git/setup.html) to get started with Git and GitHub.
* [Node.js](https://nodejs.org) >= v6.9.0
* [NPM](https://www.npmjs.com/get-npm) and
[node-gyp](https://github.com/nodejs/node-gyp#installation),
if they don't come bundled with your Node.js installation
* [Python](https://www.python.org/downloads/release/python-2713/)
(v2.7.x recommended)
* A C++ compiler compatible with C++11
* Development headers for the libXext, libXtst, and libxkbfile libraries, which can be installed using apt on Ubuntu using
```sh
$ sudo apt install libxext-dev libxtst-dev libxkbfile-dev libgconf-2-4
```
Follow our [Git Guide](https://zulip.readthedocs.io/en/latest/git/setup.html) in order to install Git, set up a GitHub account
### Ubuntu/Linux and other Debian-based distributions
On a system running Debian, Ubuntu, or another Debian-based Linux
distribution, you can install all dependencies through the package
manager (see [here][node-debian] for more on the first command):
# Install Prerequisites
```sh
$ curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
$ sudo apt install git nodejs python build-essential snapcraft libxext-dev libxtst-dev libxkbfile-dev libgconf-2-4
```
Jump to:
[node-debian]: https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions
- [MacOS](https://github.com/zulip/zulip-desktop/blob/master/development.md#macos)
- [Ubuntu/Debian](https://github.com/zulip/zulip-desktop/blob/master/development.md#ubuntudebian)
- [Windows](https://github.com/zulip/zulip-desktop/blob/master/development.md#windows)
### MacOS
On a system running MacOS, you can refer to [official nodejs docs][node-mac] to
install nodejs. To ensure Node.js has been installed, run ```node -v``` in terminal to know your node version.
## MacOS
[node-mac]: https://nodejs.org/en/download/package-manager/#macos
If [NPM](https://www.npmjs.com/get-npm) and [node-gyp](https://github.com/nodejs/node-gyp#installation) don't come bundled with your Node.js installation, you will need to install them manually.
**Node JS**
Go to the [Node.js Downloads page](https://nodejs.org/en/download/). Download Node.js for MacOS (`v6.9.0` or above recommended). Run the downloaded Node.js `.pkg` Installer. You're finished! To ensure Node.js has been installed, run `node -v` in your terminal - you should get something like `v6.9.0` or above
### Windows
- Download Node.js for Windows and install it. You can refer to the official docs [here][node-windows] to do so. To ensure Node.js has been installed, run ```node -v``` in Git Bash to know your node version.
**if** [**NPM**](https://www.npmjs.com/get-npm) **and** [**node-gyp**](https://github.com/nodejs/node-gyp#installation) **don't come bundled with your Node.js installation, Download manually**
[node-windows]: https://nodejs.org/en/download/package-manager/#windows
- Also, install install Windows-Build-Tools to compile native node modules by using
```sh
$ npm install --global windows-build-tools
```
Now you are ready for next step [: Get Zulip Desktop Code.](https://github.com/zulip/zulip-desktop/blob/master/development.md#get-zulip-desktop-code)
## Download, build, and run
Clone the source locally:
```sh
$ git clone https://github.com/zulip/zulip-desktop
$ cd zulip-desktop
```
## Ubuntu/Debian
Install project dependencies:
```sh
$ npm install
```
Start the app:
```sh
$ npm start
```
If youre in a hurry, you can copy and paste the following into your terminal
Start and watch changes:
```sh
$ npm run dev
```
sudo apt install git nodejs node-gyp python build-essential snapcraft libxext-dev libxtst-dev lib xkbfile-dev libgconf-2-4
Run tests:
```sh
$ npm test
```
after pasting you can jump to next step [: Get Zulip Desktop Code](https://github.com/zulip/zulip-desktop/blob/master/development.md#get-zulip-desktop-code).
## How to contribute?
Feel free to fork this repository, test it locally and then report any bugs
you find in the [issue tracker](https://github.com/zulip/zulip-desktop/issues).
**For a step-by-step explanation, read on.**
You can read more about making contributions in our [Contributing Guide](./CONTRIBUTING.md).
1. **Node JS**
## Troubleshooting
`$ sudo apt-get install nodejs`
If you have any problems running the app, see the [most common
issues](./troubleshooting.md).
2. **Install** [**Node-gyp**](https://github.com/nodejs/node-gyp#installation)
## Making a release
3. **Python (v2.7.x recommended)**
To package the app into an installer:
```
npm run dist
```
`$ sudo apt install python2.7`
This command will produce distributable packages or installers for the
operating system you're running on:
* on Windows, a .7z nsis file and a .exe WebSetup file
* on macOS, a `.dmg` file
* on Linux, a plain `.zip` file as well as a `.deb` file, `.snap` file and an
`AppImage` file.
4. **C++ compiler compatible with C++11**
To generate all three types of files, you will need all three operating
systems.
`$ sudo apt install build-essential`
5. **Snapcraft**
`$ sudo apt install snapcraft`
6. **Development** **headers**
`$ sudo apt install libxext-dev libxtst-dev libxkbfile-dev libgconf-2-4`
**if** [**NPM**](https://www.npmjs.com/get-npm) **don't come bundled with your Node.js installation, Download manually**
Now you are ready for next step [: Get Zulip Desktop Code.](https://github.com/zulip/zulip-desktop/blob/master/development.md#get-zulip-desktop-code)
## Windows
**Node JS**
Go to the [Node.js Downloads page](https://nodejs.org/en/download/). Download Node.js for windows (`v6.9.0` or above recommended). Run the downloaded Node.js `.msi` Installer. You're finished! To ensure Node.js has been installed, run `node -v` in your terminal - you should get something like `v6.9.0` or above
**Followings are optional yet recommended prerequisites -**
**Cmder**
1. Download the [latest release](https://github.com/cmderdev/cmder/releases/)
2. Extract the archive. *Note: This path should not be* `C:\Program Files` *or anywhere else that would require Administrator access for modifying configuration files*
3. (optional) Place your own executable files into the `%cmder_root%\bin` folder to be injected into your PATH.
4. Run `Cmder.exe`
**Chocolatey**
You can download chocolatey from here https://chocolatey.org/ and for Installing Chocolatey on your machine follow this steps
1. First, ensure that you are using an administrative shell.
2. Copy the text specific to your command shell - [cmd.exe](https://chocolatey.org/install#install-with-cmdexe) or [powershell.exe](https://chocolatey.org/install#install-with-powershellexe).
3. Paste the copied text into your shell and press Enter.
4. Wait a few seconds for the command to complete.
5. If you don't see any errors, you are ready to use Chocolatey! Type `choco` or `choco -?`
**System specific dependencies**
- use only 32bit or 64bit for all of the installers, do not mix architectures
- install using default settings
- open Windows Powershell as Admin and paste this
C:\Windows\system32> npm install --global --production windows-build-tools
**if** [**NPM**](https://www.npmjs.com/get-npm) **and** [**node-gyp**](https://github.com/nodejs/node-gyp#installation) **don't come bundled with your Node.js installation, Download manually**
Now you are ready for next step [: Get Zulip Desktop Code.](https://github.com/zulip/zulip-desktop/blob/master/development.md#get-zulip-desktop-code)
# Get Zulip Desktop Code
1. In your browser, visit https://github.com/zulip/zulip-desktop and click the `fork` button. You will need to be logged in to GitHub to do this.
2. Open Terminal (macOS/Ubuntu) or Git BASH (Windows; must **run as an Administrator**).
3. In Terminal/Git BASH, [clone your fork of the zulip-desktop repository](https://github.com/zulip/zulip-desktop/blob/master/development.md#clone-to-your-machine) and [connect the zulip-desktop upstream repository](https://github.com/zulip/zulip-desktop/blob/master/development.md#connect-your-fork-to-zulip-desktop-upstream)
## Clone to your machine
1. On GitHub, navigate to the main page of your fork repository.
2. Under the repository name, click **Clone or download**.
3. In the Clone with HTTPs section, click to copy the clone URL for the repository.
4. Open Terminal, Change the current working directory to the location where you want the cloned directory to be made.
git clone https://github.com/YOURUSERNAME/zulip-desktop.git
Dont forget to replace YOURUSERNAME with your git username
## Connect your fork to zulip-desktop upstream
cd zulip-desktop
git remote add -f upstream https://github.com/zulip/zulip-desktop.git
# build and run
## Install project dependencies:
$ npm install
## There two ways to start the app:
**vanilla method**
$ npm start
**start and watch changes recommended for devs**
$ npm run dev
The output distributable packages appear in the `dist/` directory.

8
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "zulip",
"version": "2.5.0-beta",
"version": "3.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -2838,9 +2838,9 @@
"dev": true
},
"electron": {
"version": "3.0.10",
"resolved": "https://registry.npmjs.org/electron/-/electron-3.0.10.tgz",
"integrity": "sha512-I39IeQP3NOlbjKzTDK8uK2JdiHDfhV5SruCS2Gttkn2MaKCY+yIzQ6Wr4DyBXLeTEkL1sbZxbqQVhCavAliv5w==",
"version": "3.1.10",
"resolved": "https://registry.npmjs.org/electron/-/electron-3.1.10.tgz",
"integrity": "sha512-IORdmdD5gWHmp3ffa+ZRD9kESJwdmQz8carDTeza6+W76dG447AUn7GmKk4cun31bLYTKb56D8pPhxa9S7kOZQ==",
"dev": true,
"requires": {
"@types/node": "^8.0.24",

View File

@@ -127,7 +127,7 @@
"assert": "1.4.1",
"cp-file": "5.0.0",
"devtron": "1.4.0",
"electron": "3.0.10",
"electron": "3.1.10",
"electron-builder": "20.40.2",
"electron-connect": "0.6.2",
"electron-debug": "1.4.0",