mirror of
https://github.com/zulip/zulip-desktop.git
synced 2025-10-24 00:23:36 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e21ff9d6c | ||
|
|
037cb227da | ||
|
|
88b764dcc9 | ||
|
|
9fe72c0d21 | ||
|
|
31b00ee6a6 | ||
|
|
ecec7f8b89 | ||
|
|
139496b716 | ||
|
|
f6239132c1 | ||
|
|
6451373ff6 |
@@ -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) => {
|
||||
|
||||
@@ -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
2
app/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "zulip",
|
||||
"version": "2.5.0-beta",
|
||||
"version": "3.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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%;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
72
app/renderer/js/pages/preference/find-accounts.js
Normal file
72
app/renderer/js/pages/preference/find-accounts.js
Normal 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;
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
27
app/resources/messages.js
Normal 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();
|
||||
71
changelog.md
71
changelog.md
@@ -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**:
|
||||
|
||||
202
development.md
202
development.md
@@ -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). You’ll need at least **2GB of available RAM**. Installing the Zulip electron development environment requires downloading several hundred megabytes of dependencies, so you will need an **active, reasonably fast, internet connection throughout the entire installation processes.**
|
||||
## Prerequisites
|
||||
|
||||
# Set up Git & GitHub
|
||||
To build and run the app from source, you'll need the following:
|
||||
|
||||
You can skip this step if you already have Git, GitHub.
|
||||
* [Git](http://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
|
||||
* 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 you’re 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
|
||||
|
||||
Don’t forget to replace YOURUSERNAME with your git username
|
||||
|
||||
|
||||
## Connect your fork to zulip-desktop upstream
|
||||
|
||||
cd zulip-desktop
|
||||
git remote add -f upstream https://github.com/zulip/zulip-desktop.git
|
||||
|
||||
|
||||
# build and run
|
||||
|
||||
|
||||
## Install project dependencies:
|
||||
$ npm install
|
||||
|
||||
|
||||
## There two ways to start the app:
|
||||
|
||||
**vanilla method**
|
||||
|
||||
$ npm start
|
||||
|
||||
**start and watch changes recommended for dev’s**
|
||||
|
||||
$ npm run dev
|
||||
The output distributable packages appear in the `dist/` directory.
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user