Compare commits

...

26 Commits

Author SHA1 Message Date
Akash Nimare
95a9568ece beta-release: New release v4.0.2-beta. 2019-11-13 14:56:34 +05:30
Kanishk Kakar
e7a885a1fb macos: Enable notarization for macOS Catalina.
This fixes the issue for Catalina users.
2019-11-13 14:55:11 +05:30
Akash Nimare
17d4d97e2e certificate: Make certificate location dynamic.
* certificate: Make certificate location dynamic.

* Update certificate location for old servers.
2019-11-01 19:56:22 +05:30
Akash Nimare
3b14684058 Add docs for translation 2019-10-30 02:10:38 +05:30
Akash Nimare
7d592a0a1c validation: Add SSL troubleshooting guide in error message. 2019-10-25 21:03:28 +05:30
Ross Brunton
eb1be7106b Added option to quit on closing the window.
This adds a configuration option to quit the app (rather than going to
the tray) when the "close" button is clicked.
2019-10-14 01:05:18 +05:30
Tim Abbott
1da6e5d51d README: Fix typo in link to server/webapp repository. 2019-10-07 11:51:57 -07:00
Tim Abbott
dae7089c7e README: Explicitly address where to report bugs. 2019-10-01 15:41:39 -07:00
Muskan Khedia
30b40e2ff2 network: Prompts Desktop App to ask for network setting in add-org page.
Fixes: #540.
2019-09-28 17:44:34 +05:30
Akash Nimare
b76f01349a docs: Update electron tutorial guide.
Fixes: #826.
2019-09-27 23:02:25 +05:30
Kanishk Kakar
8446deb673 sidebar: Improve UX for notification settings.
* sidebar: Disable notif settings if not logged in.

* sidebar: Activate relevant tab for notif settings.
2019-09-25 18:25:00 +05:30
Kanishk Kakar
d4b9663257 network: Tackle network issues independently.
Few changes -
* webview: Show connection failure per server.
* network: Try to reconnect diff servers.
* Fixes concern that some proxy networks may allow only specific servers
to be reachable.
* domains: Show network error on server invalidation.
* webview: Handle network errors in preload script.
Fixes: #591, #312.
2019-09-24 18:22:19 +05:30
Akash Nimare
77044fd9fa enterprise: Document the enterprise feature. 2019-09-18 23:26:44 +05:30
Kanishk Kakar
177b77f0b5 sidebar: Add option to open notification setting from the context menu. 2019-09-15 21:20:08 +05:30
Kanishk Kakar
99b154b8ae system-util: Set User-Agent from main process.
* Sets user-agent config item when the app's DOM is ready.
* App sends the right User-Agent to the server-settings API.
 
Fixes #817.
2019-09-11 16:39:56 +05:30
Kanishk Kakar
3fd8aedf81 network: Reactivate network.js script.
Fixes an issue introduced during TS migration that rendered network.ts
ineffective because exports were not defined.
2019-09-02 00:25:17 +05:30
Rhythm Sharma
b4d2e55c6f linux: Fix broken icon issue for snap package. 2019-08-29 02:20:12 +05:30
Kanishk Kakar
3c701ff518 sidebar: Load last active server before others.
Fixes: #551.
2019-08-28 14:12:01 +05:30
ViPuL
1f79a97b05 system-presence: Pass system active status to webapp.
We check user status every 15 seconds and update the status accordingly
to every organization connected. The webapp then uses this system presence data
we send to set the user status based on system activity.

Fixes #352.
2019-08-19 20:09:27 -04:00
Nikita
90e8e9a806 il8n: Fix translations for ru locales. 2019-08-17 10:52:36 -04:00
Kanishk Kakar
59ef505efd settings: Fix trailing brackets.
This accidentally slipped by in the translation work we recently did.
2019-08-17 10:49:24 -04:00
Akash Nimare
8d0a111c91 version: Update app to v4.0.1. 2019-08-17 01:41:15 +05:30
Akash Nimare
a10fa8f3ad badge-count: Show badge-count on Linux.
Added support for showing the badge counts in the Unity launcher.
This should work on elementary OS and Ubuntu.

More info -
https://github.com/electron/electron/issues/16001
https://github.com/signalapp/Signal-Desktop/issues/3387
2019-08-17 01:32:50 +05:30
Akash Nimare
39427091f5 linux: Add desktopName config for Linux.
This will help in creating .desktop file for Linux.
2019-08-17 01:31:31 +05:30
ViPuL
f8d93cf397 build: Add MSI installer support.
This PR adds MSI in the target option. MSI is useful for sysadmins. There is no support for auto-updates since it often manages by the admins. 
More info -
https://github.com/electron-userland/electron-builder/releases/tag/v19.41.0
https://github.com/electron-userland/electron-builder/issues/3322

Fixes: #641.
2019-08-15 00:35:00 +05:30
Priyank Patel
ab62b8b5bb Remove accidently commited translation-util.js file.
This file was accidently committed in 77a1fc0bd3
when migrating old PR to use typescript. This commit removes it.
2019-08-14 12:50:47 -04:00
35 changed files with 671 additions and 271 deletions

View File

@@ -6,7 +6,7 @@ The following is a set of guidelines for contributing to Zulip's desktop Client.
## Getting Started
Zulip-Desktop app is built on top of [Electron](http://electron.atom.io/). If you are new to Electron, please head over to [this](https://jlord.dev/blog/essential-electron) great article.
Zulip-Desktop app is built on top of [Electron](http://electron.atom.io/). If you are new to Electron, please head over to [this](https://jlord.us/essential-electron) great article.
## Community

View File

@@ -18,6 +18,15 @@ Please see the [installation guide](https://zulipchat.com/help/desktop-app-insta
* Multi-language spell checker
* Automatic updates
# Reporting issues
This desktop client shares most of its code with the Zulip webapp.
Issues in an individual organization's Zulip window should be reported
in the [Zulip server and webapp
project](https://github.com/zulip/zulip/issues/new). Other
issues in the desktop app and its settings should be reported [in this
project](https://github.com/zulip/zulip-desktop/issues/new).
# Contribute
First, join us on the [Zulip community server](https://zulip.readthedocs.io/en/latest/contributing/chat-zulip-org.html)!

View File

@@ -8,7 +8,7 @@ import path = require('path');
import fs = require('fs');
import isDev = require('electron-is-dev');
import electron = require('electron');
const { app, ipcMain } = electron;
const { app, ipcMain, session } = electron;
import AppMenu = require('./menu');
import BadgeSettings = require('../renderer/js/pages/preference/badge-settings');
@@ -94,6 +94,9 @@ function createMainWindow(): Electron.BrowserWindow {
// Keep the app running in background on close event
win.on('close', e => {
if (ConfigUtil.getConfigItem("quitOnClose")) {
app.quit();
}
if (!isQuitting) {
e.preventDefault();
@@ -182,6 +185,10 @@ app.on('ready', () => {
} else {
mainWindow.show();
}
if (!ConfigUtil.isConfigItemExists('userAgent')) {
const userAgent = session.fromPartition('webview:persistsession').getUserAgent();
ConfigUtil.setConfigItem('userAgent', userAgent);
}
});
page.once('did-frame-finish-load', () => {
@@ -362,6 +369,26 @@ app.on('ready', () => {
ipcMain.on('save-last-tab', (_event: Electron.IpcMessageEvent, index: number) => {
ConfigUtil.setConfigItem('lastActiveTab', index);
});
// Update user idle status for each realm after every 15s
const idleCheckInterval = 15 * 1000; // 15 seconds
setInterval(() => {
// Set user idle if no activity in 1 second (idleThresholdSeconds)
const idleThresholdSeconds = 1; // 1 second
// TODO: Remove typecast to any when types get added
// TODO: use powerMonitor.getSystemIdleState when upgrading electron
// powerMonitor.querySystemIdleState is deprecated in current electron
// version at the time of writing.
const powerMonitor = electron.powerMonitor as any;
powerMonitor.querySystemIdleState(idleThresholdSeconds, (idleState: string) => {
if (idleState === 'active') {
page.send('set-active');
} else {
page.send('set-idle');
}
});
}, idleCheckInterval);
});
app.on('before-quit', () => {

2
app/package-lock.json generated
View File

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

View File

@@ -1,7 +1,8 @@
{
"name": "zulip",
"productName": "Zulip",
"version": "4.0.0",
"version": "4.0.2-beta",
"desktopName": "zulip.desktop",
"description": "Zulip Desktop App",
"license": "Apache-2.0",
"copyright": "Kandra Labs, Inc.",
@@ -30,6 +31,7 @@
"@sentry/electron": "0.14.0",
"adm-zip": "0.4.11",
"auto-launch": "5.0.5",
"backoff": "2.5.0",
"dotenv": "8.0.0",
"electron-is-dev": "0.3.0",
"electron-log": "2.2.14",
@@ -38,7 +40,6 @@
"electron-window-state": "5.0.3",
"escape-html": "1.0.3",
"i18n": "0.8.3",
"is-online": "7.0.0",
"node-json-db": "0.9.2",
"request": "2.85.0",
"semver": "5.4.1",

View File

@@ -17,27 +17,43 @@ body {
}
#title {
text-align: left;
font-size: 24px;
font-weight: bold;
margin: 20px 0;
}
#subtitle {
font-size: 20px;
text-align: left;
margin: 12px 0;
}
#description {
text-align: left;
font-size: 16px;
list-style-position: inside;
}
#reconnect {
float: left;
}
#settings {
margin-left: 116px;
}
.button {
font-size: 16px;
background: rgba(0, 150, 136, 1.000);
color: rgba(255, 255, 255, 1.000);
width: 84px;
width: 96px;
height: 32px;
border-radius: 5px;
line-height: 32px;
margin: 20px auto 0;
cursor: pointer;
}
#reconnect:hover {
.button:hover {
opacity: 0.8;
}

View File

@@ -642,6 +642,26 @@ input.toggle-round:checked + label::after {
cursor: pointer;
}
.server-network-option {
font-weight: bold;
font-size: 1.1rem;
margin-top: 10px;
padding-top: 15px;
align-items: center;
text-align: center;
color: rgb(78, 191, 172);
width: 98%;
height: 46px;
cursor: pointer;
}
i.open-network-button {
font-size: 16px;
cursor: pointer;
padding-left: 5px;
position: absolute;
}
/* responsive grid */
@media (max-width: 650px) {

View File

@@ -25,6 +25,10 @@ class Tab extends BaseComponent {
this.$el.addEventListener('mouseout', this.props.onHoverOut);
}
showNetworkError(): void {
this.webview.forceLoad();
}
activate(): void {
this.$el.classList.add('active');
this.webview.load();

View File

@@ -125,7 +125,9 @@ class WebView extends BaseComponent {
const hasConnectivityErr = SystemUtil.connectivityERR.includes(errorDescription);
if (hasConnectivityErr) {
console.error('error', errorDescription);
this.props.onNetworkError();
if (!this.props.url.includes('network.html')) {
this.props.onNetworkError(this.props.index);
}
}
});
@@ -152,6 +154,10 @@ class WebView extends BaseComponent {
return messageCountInTitle ? Number(messageCountInTitle[1]) : 0;
}
showNotificationSettings(): void {
this.$el.executeJavaScript('showNotificationSettings()');
}
show(): void {
// Do not show WebView if another tab was selected and this tab should be in background.
if (!this.props.isActive()) {
@@ -279,6 +285,10 @@ class WebView extends BaseComponent {
this.$el.reload();
}
forceLoad(): void {
this.init();
}
send(channel: string, ...param: any[]): void {
this.$el.send(channel, ...param);
}

View File

@@ -9,7 +9,20 @@ type ListenerType = ((...args: any[]) => void);
// for the whole file.
/* eslint-disable @typescript-eslint/camelcase */
class ElectronBridge extends events {
send_notification_reply_message_supported = false;
send_notification_reply_message_supported: boolean;
idle_on_system: boolean;
last_active_on_system: number;
constructor() {
super();
this.send_notification_reply_message_supported = false;
// Indicates if the user is idle or not
this.idle_on_system = false;
// Indicates the time at which user was last active
this.last_active_on_system = Date.now();
}
send_event(eventName: string | symbol, ...args: any[]): void {
this.emit(eventName, ...args);
}

View File

@@ -23,11 +23,6 @@ import CommonUtil = require('./utils/common-util');
import EnterpriseUtil = require('./utils/enterprise-util');
import Messages = require('./../../resources/messages');
const logger = new Logger({
file: 'errors.log',
timestamp: true
});
interface FunctionalTabProps {
name: string;
materialIcon: string;
@@ -63,11 +58,17 @@ interface SettingsOptions {
};
downloadsPath: string;
showDownloadFolder: boolean;
quitOnClose: boolean;
flashTaskbarOnMessage?: boolean;
dockBouncing?: boolean;
loading?: AnyObject;
}
const logger = new Logger({
file: 'errors.log',
timestamp: true
});
const rendererDirectory = path.resolve(__dirname, '..');
type ServerOrFunctionalTab = ServerTab | FunctionalTab;
@@ -206,7 +207,8 @@ class ServerManagerView {
silent: false
},
downloadsPath: `${app.getPath('downloads')}`,
showDownloadFolder: false
showDownloadFolder: false,
quitOnClose: false
};
// Platform specific settings
@@ -312,11 +314,24 @@ class ServerManagerView {
if (servers.length > 0) {
for (let i = 0; i < servers.length; i++) {
this.initServer(servers[i], i);
DomainUtil.updateSavedServer(servers[i].url, i);
this.activateTab(i);
}
// Open last active tab
this.activateTab(ConfigUtil.getConfigItem('lastActiveTab'));
let lastActiveTab = ConfigUtil.getConfigItem('lastActiveTab');
if (lastActiveTab >= servers.length) {
lastActiveTab = 0;
}
// checkDomain() and webview.load() for lastActiveTab before the others
DomainUtil.updateSavedServer(servers[lastActiveTab].url, lastActiveTab);
this.activateTab(lastActiveTab);
for (let i = 0; i < servers.length; i++) {
// after the lastActiveTab is activated, we load the others in the background
// without activating them, to prevent flashing of server icons
if (i === lastActiveTab) {
continue;
}
DomainUtil.updateSavedServer(servers[i].url, i);
this.tabs[i].webview.load();
}
// Remove focus from the settings icon at sidebar bottom
this.$settingsButton.classList.remove('active');
} else if (this.presetOrgs.length === 0) {
@@ -357,7 +372,7 @@ class ServerManagerView {
}
this.showLoading(this.loading[this.tabs[this.activeTabIndex].webview.props.url]);
},
onNetworkError: this.openNetworkTroubleshooting.bind(this),
onNetworkError: (index: number) => this.openNetworkTroubleshooting(index),
onTitleChange: this.updateBadge.bind(this),
nodeIntegration: false,
preload: true
@@ -523,7 +538,7 @@ class ServerManagerView {
}
this.showLoading(this.loading[this.tabs[this.activeTabIndex].webview.props.url]);
},
onNetworkError: this.openNetworkTroubleshooting.bind(this),
onNetworkError: (index: number) => this.openNetworkTroubleshooting(index),
onTitleChange: this.updateBadge.bind(this),
nodeIntegration: true,
preload: false
@@ -555,12 +570,11 @@ class ServerManagerView {
});
}
openNetworkTroubleshooting(): void {
this.openFunctionalTab({
name: 'Network Troubleshooting',
materialIcon: 'network_check',
url: `file://${rendererDirectory}/network.html`
});
openNetworkTroubleshooting(index: number): void {
const reconnectUtil = new ReconnectUtil(this.tabs[index].webview);
reconnectUtil.pollInternetAndReload();
this.tabs[index].webview.props.url = `file://${rendererDirectory}/network.html`;
this.tabs[index].showNetworkError();
}
activateLastTab(index: number): void {
@@ -719,6 +733,11 @@ class ServerManagerView {
this.$dndButton.querySelector('i').textContent = alert ? 'notifications_off' : 'notifications';
}
isLoggedIn(tabIndex: number): boolean {
const url = this.tabs[tabIndex].webview.$el.src;
return !(url.endsWith('/login/') || this.tabs[tabIndex].webview.loading);
}
addContextMenu($serverImg: HTMLImageElement, index: number): void {
$serverImg.addEventListener('contextmenu', e => {
e.preventDefault();
@@ -743,6 +762,15 @@ class ServerManagerView {
});
}
},
{
label: 'Notification settings',
enabled: this.isLoggedIn(index),
click: () => {
// switch to tab whose icon was right-clicked
this.activateTab(index);
this.tabs[index].webview.showNotificationSettings();
}
},
{
label: 'Copy Zulip URL',
click: () => {
@@ -778,6 +806,10 @@ class ServerManagerView {
});
}
ipcRenderer.on('show-network-error', (event: Event, index: number) => {
this.openNetworkTroubleshooting(index);
});
ipcRenderer.on('open-settings', (event: Event, settingNav: string) => {
this.openSettings(settingNav);
});
@@ -961,22 +993,30 @@ class ServerManagerView {
ipcRenderer.on('new-server', () => {
this.openSettings('AddServer');
});
ipcRenderer.on('set-active', () => {
const webviews: NodeListOf<Electron.WebviewTag> = document.querySelectorAll('webview');
webviews.forEach(webview => {
webview.send('set-active');
});
});
ipcRenderer.on('set-idle', () => {
const webviews: NodeListOf<Electron.WebviewTag> = document.querySelectorAll('webview');
webviews.forEach(webview => {
webview.send('set-idle');
});
});
ipcRenderer.on('open-network-settings', () => {
this.openSettings('Network');
});
}
}
window.addEventListener('load', () => {
const serverManagerView = new ServerManagerView();
const reconnectUtil = new ReconnectUtil(serverManagerView);
serverManagerView.init();
window.addEventListener('online', () => {
reconnectUtil.pollInternetAndReload();
});
window.addEventListener('offline', () => {
reconnectUtil.clearState();
logger.log('No internet connection, you are offline.');
});
// only start electron-connect (auto reload on change) when its ran
// from `npm run dev` or `gulp dev` and not from `npm start` when
// app is started `npm start` main process's proces.argv will have

View File

@@ -3,19 +3,14 @@
import { ipcRenderer } from 'electron';
class NetworkTroubleshootingView {
$reconnectButton: Element;
constructor() {
this.$reconnectButton = document.querySelector('#reconnect');
}
init(): void {
this.$reconnectButton.addEventListener('click', () => {
init($reconnectButton: Element, $settingsButton: Element): void {
$reconnectButton.addEventListener('click', () => {
ipcRenderer.send('forward-message', 'reload-viewer');
});
$settingsButton.addEventListener('click', () => {
ipcRenderer.send('forward-message', 'open-settings');
});
}
}
window.addEventListener('load', () => {
const networkTroubleshootingView = new NetworkTroubleshootingView();
networkTroubleshootingView.init();
});
export = new NetworkTroubleshootingView();

View File

@@ -18,11 +18,11 @@ class BadgeSettings {
}
showBadgeCount(messageCount: number, mainWindow: electron.BrowserWindow): void {
if (process.platform === 'darwin') {
app.setBadgeCount(messageCount);
}
if (process.platform === 'win32') {
this.updateOverlayIcon(messageCount, mainWindow);
} else {
// This should work on both macOS and Linux
app.setBadgeCount(messageCount);
}
}

View File

@@ -42,7 +42,7 @@ class GeneralSection extends BaseSection {
<div class="setting-control"></div>
</div>
<div class="setting-row" id="dock-bounce-option" style= "display:${process.platform === 'darwin' ? '' : 'none'}">
<div class="setting-description">${t.__('Bounce dock on new private message')})}</div>
<div class="setting-description">${t.__('Bounce dock on new private message')}</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="flash-taskbar-option" style= "display:${process.platform === 'win32' ? '' : 'none'}">
@@ -82,6 +82,10 @@ class GeneralSection extends BaseSection {
<div class="setting-description">${t.__('Always start minimized')}</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="quitOnClose-option">
<div class="setting-description">${t.__('Quit when the window is closed')}</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="enable-spellchecker-option">
<div class="setting-description">${t.__('Enable spellchecker (requires restart)')}</div>
<div class="setting-control"></div>
@@ -155,6 +159,7 @@ class GeneralSection extends BaseSection {
this.removeCustomCSS();
this.downloadFolder();
this.showDownloadFolder();
this.updateQuitOnCloseOption();
this.enableErrorReporting();
// Platform specific settings
@@ -321,6 +326,18 @@ class GeneralSection extends BaseSection {
});
}
updateQuitOnCloseOption(): void {
this.generateSettingOption({
$element: document.querySelector('#quitOnClose-option .setting-control'),
value: ConfigUtil.getConfigItem('quitOnClose', false),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('quitOnClose');
ConfigUtil.setConfigItem('quitOnClose', newValue);
this.updateQuitOnCloseOption();
}
});
}
enableSpellchecker(): void {
this.generateSettingOption({
$element: document.querySelector('#enable-spellchecker-option .setting-control'),

View File

@@ -1,6 +1,6 @@
'use strict';
import { shell } from 'electron';
import { shell, ipcRenderer } from 'electron';
import BaseComponent = require('../../components/base');
import DomainUtil = require('../../utils/domain-util');
@@ -30,15 +30,21 @@ class NewServerForm extends BaseComponent {
</div>
</div>
<div class="server-center">
<div class="divider">
<hr class="left"/>${t.__('OR')}<hr class="right" />
</div>
<div class="divider">
<hr class="left"/>${t.__('OR')}<hr class="right" />
</div>
</div>
<div class="server-center">
<div class="server-save-action">
<button id="open-create-org-link">${t.__('Create a new organization')}</button>
</div>
<div class="server-save-action">
<button id="open-create-org-link">${t.__('Create a new organization')}</button>
</div>
</div>
<div class="server-center">
<div class="server-network-option">
<span id="open-network-settings">${t.__('Network and Proxy Settings')}</span>
<i class="material-icons open-network-button">open_in_new</i>
</div>
</div>
</div>
`;
}
@@ -76,6 +82,11 @@ class NewServerForm extends BaseComponent {
});
}
networkSettingsLink(): void {
const networkSettingsId = document.querySelectorAll('.server-network-option')[0];
networkSettingsId.addEventListener('click', () => ipcRenderer.send('forward-message', 'open-network-settings'));
}
initActions(): void {
this.$saveServerButton.addEventListener('click', () => {
this.submitFormHandler();
@@ -89,6 +100,7 @@ class NewServerForm extends BaseComponent {
});
// open create new org link in default browser
this.openCreateNewOrgExternalLink();
this.networkSettingsLink();
}
}

View File

@@ -1,14 +1,23 @@
// we have and will have some non camelcase stuff
// while working with zulip and electron bridge
// so turning the rule off for the whole file.
/* eslint-disable @typescript-eslint/camelcase */
'use strict';
import { ipcRenderer, shell } from 'electron';
import SetupSpellChecker from './spellchecker';
import isDev = require('electron-is-dev');
import LinkUtil = require('./utils/link-util');
import params = require('./utils/params-util');
import NetworkError = require('./pages/network');
interface PatchedGlobal extends NodeJS.Global {
logout: () => void;
shortcut: () => void;
showNotificationSettings: () => void;
}
const globalPatched = global as PatchedGlobal;
@@ -21,7 +30,6 @@ require(__dirname + '/shared/preventdrag.js');
declare let window: ZulipWebWindow;
// eslint-disable-next-line @typescript-eslint/camelcase
window.electron_bridge = require('./electron-bridge');
const logout = (): void => {
@@ -46,16 +54,33 @@ const shortcut = (): void => {
}
};
const showNotificationSettings = (): void => {
// Create the menu for the below
const dropdown: HTMLElement = document.querySelector('.dropdown-toggle');
dropdown.click();
const nodes: NodeListOf<HTMLElement> = document.querySelectorAll('.dropdown-menu li a');
nodes[2].click();
const notificationItem: NodeListOf<HTMLElement> = document.querySelectorAll('.normal-settings-list li div');
// wait until the notification dom element shows up
setTimeout(() => {
notificationItem[2].click();
}, 100);
};
process.once('loaded', (): void => {
globalPatched.logout = logout;
globalPatched.shortcut = shortcut;
globalPatched.showNotificationSettings = showNotificationSettings;
});
// To prevent failing this script on linux we need to load it after the document loaded
document.addEventListener('DOMContentLoaded', (): void => {
if (params.isPageParams()) {
// Get the default language of the server
const serverLanguage = page_params.default_language; // eslint-disable-line no-undef, @typescript-eslint/camelcase
const serverLanguage = page_params.default_language; // eslint-disable-line no-undef
if (serverLanguage) {
// Init spellchecker
SetupSpellChecker.init(serverLanguage);
@@ -97,6 +122,15 @@ window.addEventListener('beforeunload', (): void => {
SetupSpellChecker.unsubscribeSpellChecker();
});
window.addEventListener('load', (event: any): void => {
if (!event.target.URL.includes('app/renderer/network.html')) {
return;
}
const $reconnectButton = document.querySelector('#reconnect');
const $settingsButton = document.querySelector('#settings');
NetworkError.init($reconnectButton, $settingsButton);
});
// electron's globalShortcut can cause unexpected results
// so adding the reload shortcut in the old-school way
// Zoom from numpad keys is not supported by electron, so adding it through listeners.
@@ -112,3 +146,20 @@ document.addEventListener('keydown', event => {
ipcRenderer.send('forward-message', 'zoomActualSize');
}
});
// Set user as active and update the time of last activity
ipcRenderer.on('set-active', () => {
if (isDev) {
console.log('active');
}
window.electron_bridge.idle_on_system = false;
window.electron_bridge.last_active_on_system = Date.now();
});
// Set user as idle and time of last activity is left unchanged
ipcRenderer.on('set-idle', () => {
if (isDev) {
console.log('idle');
}
window.electron_bridge.idle_on_system = true;
});

View File

@@ -63,7 +63,7 @@ class CertificateUtil {
}
setCertificate(server: string, fileName: string): void {
const filePath = `${certificatesDir}/${fileName}`;
const filePath = `${fileName}`;
this.db.push(`/${server}`, filePath, true);
this.reloadDB();
}

View File

@@ -12,6 +12,7 @@ import RequestUtil = require('./request-util');
import EnterpriseUtil = require('./enterprise-util');
import Messages = require('../../../resources/messages');
const { ipcRenderer } = electron;
const { app, dialog } = electron.remote;
const logger = new Logger({
@@ -59,6 +60,16 @@ class DomainUtil {
return this.db.getData(`/domains[${index}]`);
}
shouldIgnoreCerts(url: string): boolean {
const domains = this.getDomains();
for (const domain of domains) {
if (domain.url === url) {
return domain.ignoreCerts;
}
}
return null;
}
updateDomain(index: number, server: object): void {
this.reloadDB();
this.db.push(`/domains[${index}]`, server, true);
@@ -250,19 +261,21 @@ class DomainUtil {
});
}
updateSavedServer(url: string, index: number): void {
async updateSavedServer(url: string, index: number): Promise<void> {
// Does not promise successful update
const oldIcon = this.getDomain(index).icon;
const { ignoreCerts } = this.getDomain(index);
this.checkDomain(url, ignoreCerts, true).then(newServerConf => {
this.saveServerIcon(newServerConf, ignoreCerts).then(localIconUrl => {
if (!oldIcon || localIconUrl !== '../renderer/img/icon.png') {
newServerConf.icon = localIconUrl;
this.updateDomain(index, newServerConf);
this.reloadDB();
}
});
});
try {
const newServerConf = await this.checkDomain(url, ignoreCerts, true);
const localIconUrl = await this.saveServerIcon(newServerConf, ignoreCerts);
if (!oldIcon || localIconUrl !== '../renderer/img/icon.png') {
newServerConf.icon = localIconUrl;
this.updateDomain(index, newServerConf);
this.reloadDB();
}
} catch (err) {
ipcRenderer.send('forward-message', 'show-network-error', index);
}
}
reloadDB(): void {

View File

@@ -1,5 +1,10 @@
import isOnline = require('is-online');
import { ipcRenderer } from 'electron';
import backoff = require('backoff');
import request = require('request');
import Logger = require('./logger-util');
import RequestUtil = require('./request-util');
import DomainUtil = require('./domain-util');
const logger = new Logger({
file: `domain-util.log`,
@@ -7,43 +12,73 @@ const logger = new Logger({
});
class ReconnectUtil {
// TODO: TypeScript - Figure out how to annotate serverManagerView
// it should be ServerManagerView; maybe make it a generic so we can
// pass the class from main.js
serverManagerView: any;
// TODO: TypeScript - Figure out how to annotate webview
// it should be WebView; maybe make it a generic so we can
// pass the class from main.ts
webview: any;
url: string;
alreadyReloaded: boolean;
fibonacciBackoff: any;
constructor(serverManagerView: any) {
this.serverManagerView = serverManagerView;
constructor(webview: any) {
this.webview = webview;
this.url = webview.props.url;
this.alreadyReloaded = false;
this.clearState();
}
clearState(): void {
this.alreadyReloaded = false;
this.fibonacciBackoff = backoff.fibonacci({
initialDelay: 5000,
maxDelay: 300000
});
}
isOnline(): Promise<boolean> {
return new Promise(resolve => {
try {
const ignoreCerts = DomainUtil.shouldIgnoreCerts(this.url);
if (ignoreCerts === null) {
return;
}
request(
{
url: `${this.url}/static/favicon.ico`,
...RequestUtil.requestOptions(this.url, ignoreCerts)
},
(error: Error, response: any) => {
const isValidResponse =
!error && response.statusCode >= 200 && response.statusCode < 400;
resolve(isValidResponse);
}
);
} catch (err) {
logger.log(err);
}
});
}
pollInternetAndReload(): void {
const pollInterval = setInterval(() => {
this._checkAndReload()
.then(status => {
if (status) {
this.alreadyReloaded = true;
clearInterval(pollInterval);
}
});
}, 1500);
this.fibonacciBackoff.backoff();
this.fibonacciBackoff.on('ready', () => {
this._checkAndReload().then(status => {
if (status) {
this.fibonacciBackoff.reset();
} else {
this.fibonacciBackoff.backoff();
}
});
});
}
// TODO: Make this a async function
_checkAndReload(): Promise<boolean> {
return new Promise(resolve => {
if (!this.alreadyReloaded) { // eslint-disable-line no-negated-condition
isOnline()
this.isOnline()
.then((online: boolean) => {
if (online) {
if (!this.alreadyReloaded) {
this.serverManagerView.reloadCurrentView();
}
ipcRenderer.send('forward-message', 'reload-viewer');
logger.log('You\'re back online.');
return resolve(true);
}

View File

@@ -1,10 +1,15 @@
import { remote } from 'electron';
import fs = require('fs');
import path = require('path');
import ConfigUtil = require('./config-util');
import Logger = require('./logger-util');
import ProxyUtil = require('./proxy-util');
import CertificateUtil = require('./certificate-util');
import SystemUtil = require('./system-util');
const { app } = remote;
const logger = new Logger({
file: `request-util.log`,
timestamp: true
@@ -38,11 +43,20 @@ class RequestUtil {
const certificate = CertificateUtil.getCertificate(
encodeURIComponent(domain)
);
let certificateFile = null;
if (certificate && certificate.includes('/')) {
// certificate saved using old app version
certificateFile = certificate;
} else if (certificate) {
certificateFile = path.join(`${app.getPath('userData')}/certificates`, certificate);
}
let certificateLocation = '';
if (certificate) {
// To handle case where certificate has been moved from the location in certificates.json
try {
certificateLocation = fs.readFileSync(certificate, 'utf8');
certificateLocation = fs.readFileSync(certificateFile, 'utf8');
} catch (err) {
logger.warn(`Error while trying to get certificate: ${err}`);
}

View File

@@ -2,6 +2,7 @@
import { remote } from 'electron';
import os = require('os');
import ConfigUtil = require('./config-util');
const { app } = remote;
let instance: null | SystemUtil = null;
@@ -53,6 +54,9 @@ class SystemUtil {
}
getUserAgent(): string | null {
if (!this.userAgent) {
this.setUserAgent(ConfigUtil.getConfigItem('userAgent', null));
}
return this.userAgent;
}
}

View File

@@ -1,29 +0,0 @@
'use strict';
const path = require("path");
const electron = require("electron");
const i18n = require("i18n");
let instance = null;
let app = null;
/* To make the util runnable in both main and renderer process */
if (process.type === 'renderer') {
app = electron.remote.app;
}
else {
app = electron.app;
}
class TranslationUtil {
constructor() {
if (instance) {
return this;
}
instance = this;
i18n.configure({
directory: path.join(__dirname, '../../../translations/'),
register: this
});
}
__(phrase) {
return i18n.__({ phrase, locale: app.getLocale() });
}
}
module.exports = new TranslationUtil();

View File

@@ -9,14 +9,21 @@
<body>
<div id="content">
<div id="picture"><img src="img/zulip_network.png"></div>
<div id="title">Zulip can't connect</div>
<div id="description">
<div>Your computer seems to be offline.</div>
<div>We will keep trying to reconnect, or you can try now.</div>
<div id="title">We can't connect to this organization</div>
<div id="subtitle">This could be because</div>
<ul id="description">
<li>You're not online or your proxy is misconfigured.</li>
<li>There is no Zulip organization hosted at this URL.</li>
<li>This Zulip organization is temporarily unavailable.</li>
<li>This Zulip organization has been moved or deleted.</li>
</ul>
<div id="buttons">
<div id="reconnect" class="button">Reconnect</div>
<div id="settings" class="button">Settings</div>
</div>
<div id="reconnect">Try now</div>
</div>
</body>
<script src="js/pages/network.js"></script>
<script>var exports = {};</script>
<script src="js/preload.js"></script>
<script>require('./js/shared/preventdrag.js')</script>
</html>

View File

@@ -9,7 +9,9 @@ class Messages {
\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).`;
\n • The server has a valid certificate. (You can add custom certificates in Settings > Organizations). \
\n • The SSL is correctly configured for the certificate. Check out the SSL troubleshooting guide -
\n https://zulip.readthedocs.io/en/stable/production/ssl-certificates.html`;
}
noOrgsError(domain: string): string {

View File

@@ -0,0 +1,20 @@
# How to help translate Zulip Desktop
These are *generated* files (*) that contain translations of the strings in
the app.
You can help translate Zulip Desktop into your language! We do our
translations in Transifex, which is a nice web app for collaborating on
translations; a maintainer then syncs those translations into this repo.
To help out, [join the Zulip project on
Transifex](https://www.transifex.com/zulip/zulip/) and enter translations
there. More details in the [Zulip contributor docs](https://zulip.readthedocs.io/en/latest/translating/translating.html#translators-workflow).
Within that Transifex project, if you'd like to focus on Zulip Desktop, look
at `desktop.json`. The other resources there are for the Zulip web/mobile
app, where translations are also very welcome.
(*) One file is an exception: `en.json` is manually maintained as a
list of (English) messages in the source code, and is used when we upload to
Transifex a list of strings to be translated. It doesn't contain any
translations.

118
app/translations/en-GB.json Normal file
View File

@@ -0,0 +1,118 @@
{
"Add Organization": "Add Organization",
"Toggle Do Not Disturb": "Toggle Do Not Disturb",
"Desktop Settings": "Desktop Settings",
"Keyboard Shortcuts": "Keyboard Shortcuts",
"Copy Zulip URL": "Copy Zulip URL",
"Log Out of Organization": "Log Out of Organization",
"Services": "Services",
"Hide": "Hide",
"Hide Others": "Hide Others",
"Unhide": "Unhide",
"Minimize": "Minimize",
"Close": "Close",
"Quit": "Quit",
"Edit": "Edit",
"Undo": "Undo",
"Redo": "Redo",
"Cut": "Cut",
"Copy": "Copy",
"Paste": "Paste",
"Paste and Match Style": "Paste and Match Style",
"Select All": "Select All",
"View": "View",
"Reload": "Reload",
"Hard Reload": "Hard Reload",
"Toggle Full Screen": "Toggle Full Screen",
"Zoom In": "Zoom In",
"Zoom Out": "Zoom Out",
"Actual Size": "Actual Size",
"Toggle Tray Icon": "Toggle Tray Icon",
"Toggle Sidebar": "Toggle Sidebar",
"Auto hide Menu bar": "Auto hide Menu bar",
"History": "History",
"Back": "Back",
"Forward": "Forward",
"Window": "Window",
"Tools": "Tools",
"Check for Updates": "Check for Updates",
"Release Notes": "Release Notes",
"Factory Reset": "Factory Reset",
"Download App Logs": "Download App Logs",
"Toggle DevTools for Zulip App": "Toggle DevTools for Zulip App",
"Toggle DevTools for Active Tab": "Toggle DevTools for Active Tab",
"Help": "Help",
"About Zulip": "About Zulip",
"Help Center": "Help Center",
"Report an Issue": "Report an Issue",
"Switch to Next Organization": "Switch to Next Organization",
"Switch to Previous Organization": "Switch to Previous Organization",
"General": "General",
"Network": "Network",
"AddServer": "AddServer",
"Organizations": "Organizations",
"Shortcuts": "Shortcuts",
"Settings": "Settings",
"Add a Zulip organization": "Add a Zulip organization",
"Organization URL": "Organization URL",
"Connect": "Connect",
"OR": "OR",
"Create a new organization": "Create a new organization",
"Proxy": "Proxy",
"Use system proxy settings (requires restart)": "Use system proxy settings (requires restart)",
"Manual proxy configuration": "Manual proxy configuration",
"script": "script",
"Proxy rules": "Proxy rules",
"Proxy bypass rules": "Proxy bypass rules",
"Save": "Save",
"Appearance": "Appearance",
"Show app icon in system tray": "Show app icon in system tray",
"Auto hide menu bar (Press Alt key to display)": "Auto hide menu bar (Press Alt key to display)",
"Show sidebar": "Show sidebar",
"Show app unread badge": "Show app unread badge",
"Bounce dock on new private message": "Bounce dock on new private message",
"Flash taskbar on new message": "Flash taskbar on new message",
"Desktop Notifications": "Desktop Notifications",
"Show desktop notifications": "Show desktop notifications",
"Mute all sounds from Zulip": "Mute all sounds from Zulip",
"App Updates": "App Updates",
"Enable auto updates": "Enable auto updates",
"Get beta updates": "Get beta updates",
"Functionality": "Functionality",
"Start app at login": "Start app at login",
"Always start minimized": "Always start minimized",
"Enable spellchecker (requires restart)": "Enable spellchecker (requires restart)",
"Advanced": "Advanced",
"Enable error reporting (requires restart)": "Enable error reporting (requires restart)",
"Show downloaded files in file manager": "Show downloaded files in file manager",
"Add custom CSS": "Add custom CSS",
"Upload": "Upload",
"Delete": "Delete",
"Default download location": "Default download location",
"Change": "Change",
"Reset Application Data": "Reset Application Data",
"This will delete all application data including all added accounts and preferences": "This will delete all application data including all added accounts and preferences",
"Reset App Data": "Reset App Data",
"Connected organizations": "Connected organizations",
"All the connected orgnizations will appear here.": "All the connected orgnizations will appear here.",
"Connect to another organization": "Connect to another organization",
"Add Custom Certificates": "Add Custom Certificates",
"Find accounts by email": "Find accounts by email",
"All the connected orgnizations will appear here": "All the connected orgnizations will appear here",
"Disconnect": "Disconnect",
"Certificate file": "Certificate file",
"Find accounts": "Find accounts",
"Tip": "Tip",
"These desktop app shortcuts extend the Zulip webapp's": "These desktop app shortcuts extend the Zulip webapp's",
"keyboard shortcuts": "keyboard shortcuts",
"Application Shortcuts": "Application Shortcuts",
"Reset App Settings": "Reset App Settings",
"Log Out": "Log Out",
"Hide Zulip": "Hide Zulip",
"Quit Zulip": "Quit Zulip",
"Edit Shortcuts": "Edit Shortcuts",
"Emoji & Symbols": "Emoji & Symbols",
"View Shortcuts": "View Shortcuts",
"Enter Full Screen": "Enter Full Screen",
"History Shortcuts": "History Shortcuts"
}

View File

@@ -105,4 +105,4 @@
"View Shortcuts": "View Shortcuts",
"History Shortcuts": "History Shortcuts",
"Window Shortcuts": "Window Shortcuts"
}
}

View File

@@ -113,5 +113,6 @@
"Zoom Out": "Zoom Out",
"Zulip Help": "Zulip Help",
"keyboard shortcuts": "keyboard shortcuts",
"script": "script"
"script": "script",
"Quit when the window is closed": "Quit when the window is closed"
}

View File

@@ -1,58 +1,58 @@
{
"About Zulip": "О Зулип",
"About Zulip": "О Zulip",
"Actual Size": "Фактический размер",
"Add Custom Certificates": "Добавить пользовательские сертификаты",
"Add Organization": "Добавить организацию",
"Add a Zulip organization": "Добавить организацию Zulip",
"Add custom CSS": "Добавить собственный CSS",
"Advanced": "продвинутый",
"Advanced": "Расширенные",
"All the connected organizations will appear here": "Все связанные организации появятся здесь",
"Always start minimized": "Всегда начинайте сворачивать",
"App Updates": "Обновления приложений",
"Appearance": "Внешность",
"Application Shortcuts": "Ярлыки приложений",
"Always start minimized": "Всегда запускаться свернутым",
"App Updates": "Обновления приложения",
"Appearance": "Внешний вид",
"Application Shortcuts": "Горячие клавиши приложения",
"Are you sure you want to disconnect this organization?": "Вы уверены, что хотите отключить эту организацию?",
"Auto hide Menu bar": "Авто скрыть панель меню",
"Auto hide menu bar (Press Alt key to display)": "Автоматическое скрытие строки меню (нажмите клавишу Alt для отображения)",
"Back": "назад",
"Bounce dock on new private message": "Отскок док на новое личное сообщение",
"Back": "Назад",
"Bounce dock on new private message": "Прыгающий док на новое личное сообщение",
"Certificate file": "Файл сертификата",
"Change": "+ Изменить",
"Check for Updates": "Проверить наличие обновлений",
"Close": "близко",
"Connect": "соединять",
"Close": "Закрыть",
"Connect": "Подключиться",
"Connect to another organization": "Подключиться к другой организации",
"Connected organizations": "Связанные организации",
"Copy": "копия",
"Copy": "Копировать",
"Copy Zulip URL": "Скопируйте Zulip URL",
"Create a new organization": "Создать новую организацию",
"Cut": "Резать",
"Default download location": "Местоположение загрузки по умолчанию",
"Delete": "удалять",
"Desktop App Settings": "Настройки настольного приложения",
"Desktop Notifications": "Уведомления на рабочем столе",
"Desktop Settings": "Настройки рабочего стола",
"Cut": "Вырезать",
"Default download location": "Местоположение загрузок по умолчанию",
"Delete": "Удалить",
"Desktop App Settings": "Настройки приложения",
"Desktop Notifications": "Уведомления в приложении",
"Desktop Settings": "Настройки приложения",
"Disconnect": "Отключить",
"Download App Logs": "Скачать журналы приложений",
"Edit": "редактировать",
"Edit Shortcuts": "Изменить ярлыки",
"Download App Logs": "Скачать журналы приложения",
"Edit": "Правка",
"Edit Shortcuts": "Изменить горячие клавиши",
"Enable auto updates": "Включить автообновления",
"Enable error reporting (requires restart)": "Включить отчет об ошибках (требуется перезагрузка)",
"Enable spellchecker (requires restart)": "Включить проверку орфографии (требуется перезагрузка)",
"Factory Reset": "Сброс к заводским настройкам",
"File": "файл",
"File": "Файл",
"Find accounts": "Найти аккаунты",
"Find accounts by email": "Найти аккаунты по электронной почте",
"Flash taskbar on new message": "Flash-панель задач на новое сообщение",
"Forward": "Вперед",
"Functionality": "функциональность",
"General": "генеральный",
"Functionality": "Функциональность",
"General": "Общий",
"Get beta updates": "Получить бета-обновления",
"Hard Reload": "Жесткая перезагрузка",
"Help": "Помогите",
"Hard Reload": "Принудительная перезагрузка",
"Help": "Помощь",
"Help Center": "Центр помощи",
"History": "история",
"History Shortcuts": "Ярлыки истории",
"History": "История",
"History Shortcuts": "Горячие клавиши истории",
"Keyboard Shortcuts": "Горячие клавиши",
"Log Out": "Выйти",
"Log Out of Organization": "Выйти из организации",
@@ -61,28 +61,28 @@
"Mute all sounds from Zulip": "Отключить все звуки от Zulip",
"NO": "НЕТ",
"Network": "сеть",
"OR": "ИЛИ ЖЕ",
"OR": "ИЛИ",
"Organization URL": "URL организации",
"Organizations": "организации",
"Organizations": "Организации",
"Paste": "Вставить",
"Paste and Match Style": "Вставить и соответствовать стилю",
"Proxy": "полномочие",
"Proxy": "Прокси",
"Proxy bypass rules": "Правила обхода прокси",
"Proxy rules": "Правила прокси",
"Quit": "Уволиться",
"Proxy rules": "Правила проксирования",
"Quit": "Выйти",
"Quit Zulip": "Выйти из Zulip",
"Redo": "переделывать",
"Redo": "Повторить",
"Release Notes": "Примечания к выпуску",
"Reload": "перезагружать",
"Reload": "Перезагрузить",
"Report an Issue": "Сообщить о проблеме",
"Reset App Data": "Сбросить данные приложения",
"Reset App Settings": "Сбросить настройки приложения",
"Reset Application Data": "Сбросить данные приложения",
"Save": "Сохранить",
"Select All": "Выбрать все",
"Settings": "настройки",
"Shortcuts": "Ярлыки",
"Show App Logs": "Показать журналы приложений",
"Settings": "Настройки",
"Shortcuts": "Горячие клавиши",
"Show App Logs": "Показать журналы приложения",
"Show app icon in system tray": "Показать значок приложения в системном трее",
"Show app unread badge": "Показать непрочитанный значок приложения",
"Show desktop notifications": "Показывать уведомления на рабочем столе",
@@ -91,25 +91,25 @@
"Start app at login": "Запустить приложение при входе",
"Switch to Next Organization": "Переключиться на следующую организацию",
"Switch to Previous Organization": "Переключиться на предыдущую организацию",
"These desktop app shortcuts extend the Zulip webapp's": "Эти ярлыки настольных приложений расширяют возможности веб-приложения Zulip.",
"These desktop app shortcuts extend the Zulip webapp's": "Эти горячие клавиши приложения расширяют возможности Zulip.",
"This will delete all application data including all added accounts and preferences": "Это удалит все данные приложения, включая все добавленные учетные записи и настройки",
"Tip": "Совет",
"Toggle DevTools for Active Tab": "Переключить DevTools для активной вкладки",
"Toggle DevTools for Zulip App": "Переключить DevTools для приложения Zulip",
"Toggle Do Not Disturb": "Переключить не беспокоить",
"Toggle Do Not Disturb": "Переключить в режим не беспокоить",
"Toggle Full Screen": "Включить полноэкранный режим",
"Toggle Sidebar": "Переключить боковую панель",
"Toggle Tray Icon": "Значок переключателя в трее",
"Tools": "инструменты",
"Undo": "расстегивать",
"Toggle Tray Icon": "Переключить Значок в трее",
"Tools": "Инструменты",
"Undo": "Повторить",
"Upload": "Загрузить",
"Use system proxy settings (requires restart)": "Использовать настройки системного прокси (требуется перезагрузка)",
"View": "Посмотреть",
"View Shortcuts": "Посмотреть ярлыки",
"View": "Вид",
"View Shortcuts": "Посмотреть горячие клавиши окна",
"Window": "Окно",
"Window Shortcuts": "Ярлыки окон",
"Window Shortcuts": "Горячие клавиши окна",
"YES": "ДА",
"Zoom In": "Приблизить",
"Zoom In": "Увеличить",
"Zoom Out": "Уменьшить",
"Zulip Help": "Zulip Помощь",
"keyboard shortcuts": "горячие клавиши",

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
</dict>
</plist>

View File

@@ -24,4 +24,4 @@ If you'd like to remove organizations and have admin access, you'll need to chan
It also turns off automatic updates for every Zulip user on the same machine.
> Currently, we only support `presetOrganizations` and `autoUpdate` settings. We are working on other settings as well, and will update this page when we add support for more.
Currently, we only support `presetOrganizations` and `autoUpdate` settings. We are working on other settings as well, and will update this page when we add support for more.

133
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "zulip",
"version": "3.1.0-beta",
"version": "4.0.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -1712,7 +1712,8 @@
"capture-stack-trace": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz",
"integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0="
"integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=",
"dev": true
},
"caseless": {
"version": "0.12.0",
@@ -2401,6 +2402,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
"integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=",
"dev": true,
"requires": {
"capture-stack-trace": "^1.0.0"
}
@@ -2876,7 +2878,8 @@
"duplexer3": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
"integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
"integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=",
"dev": true
},
"duplexify": {
"version": "3.6.0",
@@ -5378,7 +5381,8 @@
"get-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
"dev": true
},
"get-value": {
"version": "2.0.6",
@@ -5649,79 +5653,11 @@
}
}
},
"google-translate-api": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/google-translate-api/-/google-translate-api-2.3.0.tgz",
"integrity": "sha1-YmcwoSPaDVevNzXdcHzacc7pGcc=",
"dev": true,
"requires": {
"configstore": "^2.0.0",
"got": "^6.3.0",
"safe-eval": "^0.3.0"
},
"dependencies": {
"configstore": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/configstore/-/configstore-2.1.0.tgz",
"integrity": "sha1-c3o6cDbpiGECqmCZ5HuzOrGroaE=",
"requires": {
"dot-prop": "^3.0.0",
"graceful-fs": "^4.1.2",
"mkdirp": "^0.5.0",
"object-assign": "^4.0.1",
"os-tmpdir": "^1.0.0",
"osenv": "^0.1.0",
"uuid": "^2.0.1",
"write-file-atomic": "^1.1.2",
"xdg-basedir": "^2.0.0"
}
},
"dot-prop": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz",
"integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=",
"requires": {
"is-obj": "^1.0.0"
}
},
"google-translate-token": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/google-translate-token/-/google-translate-token-1.0.0.tgz",
"integrity": "sha1-vpQ0RXhvAMN2Xewx9JDawhIo0/g=",
"requires": {
"configstore": "^2.0.0",
"got": "^6.3.0"
}
},
"uuid": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz",
"integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho="
},
"write-file-atomic": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz",
"integrity": "sha1-+Aek8LHZ6ROuekgRLmzDrxmRtF8=",
"requires": {
"graceful-fs": "^4.1.11",
"imurmurhash": "^0.1.4",
"slide": "^1.1.5"
}
},
"xdg-basedir": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-2.0.0.tgz",
"integrity": "sha1-7byQPMOF/ARSPZZqM1UEtVBNG9I=",
"requires": {
"os-homedir": "^1.0.0"
}
}
}
},
"got": {
"version": "6.7.1",
"resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
"integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
"dev": true,
"requires": {
"create-error-class": "^3.0.0",
"duplexer3": "^0.1.4",
@@ -5739,7 +5675,8 @@
"graceful-fs": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
"dev": true
},
"grapheme-splitter": {
"version": "1.0.4",
@@ -6519,7 +6456,8 @@
"imurmurhash": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
"dev": true
},
"indent-string": {
"version": "2.1.0",
@@ -6914,7 +6852,8 @@
"is-obj": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8="
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
"dev": true
},
"is-obj-prop": {
"version": "1.0.0",
@@ -6986,7 +6925,8 @@
"is-redirect": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz",
"integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ="
"integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=",
"dev": true
},
"is-regex": {
"version": "1.0.4",
@@ -7015,12 +6955,14 @@
"is-retry-allowed": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz",
"integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ="
"integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=",
"dev": true
},
"is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
"dev": true
},
"is-supported-regexp-flag": {
"version": "1.0.1",
@@ -7666,7 +7608,8 @@
"lowercase-keys": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz",
"integrity": "sha1-b54wtHCE2XGnyCD/FabFFnt0wm8="
"integrity": "sha1-b54wtHCE2XGnyCD/FabFFnt0wm8=",
"dev": true
},
"lru-cache": {
"version": "4.1.3",
@@ -7906,6 +7849,7 @@
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz",
"integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=",
"dev": true,
"requires": {
"minimist": "0.0.8"
},
@@ -7913,7 +7857,8 @@
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
}
}
},
@@ -8169,7 +8114,8 @@
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"dev": true
},
"object-copy": {
"version": "0.1.0",
@@ -8412,7 +8358,8 @@
"os-homedir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
"dev": true
},
"os-locale": {
"version": "3.1.0",
@@ -8498,12 +8445,14 @@
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"dev": true
},
"osenv": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
"integrity": "sha1-hc36+uso6Gd/QW4odZK18/SepBA=",
"dev": true,
"requires": {
"os-homedir": "^1.0.0",
"os-tmpdir": "^1.0.0"
@@ -9157,7 +9106,8 @@
"prepend-http": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw="
"integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
"dev": true
},
"prettier": {
"version": "1.18.2",
@@ -9906,7 +9856,8 @@
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true
},
"safe-regex": {
"version": "1.1.0",
@@ -10055,7 +10006,8 @@
"slide": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
"integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc="
"integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=",
"dev": true
},
"snapdragon": {
"version": "0.8.2",
@@ -11473,7 +11425,8 @@
"timed-out": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz",
"integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8="
"integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=",
"dev": true
},
"tmp": {
"version": "0.0.33",
@@ -11967,7 +11920,8 @@
"unzip-response": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz",
"integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c="
"integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=",
"dev": true
},
"upath": {
"version": "1.0.5",
@@ -12038,6 +11992,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz",
"integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=",
"dev": true,
"requires": {
"prepend-http": "^1.0.1"
}

View File

@@ -1,7 +1,7 @@
{
"name": "zulip",
"productName": "Zulip",
"version": "4.0.0",
"version": "4.0.2-beta",
"main": "./app/main",
"description": "Zulip Desktop App",
"license": "Apache-2.0",
@@ -43,6 +43,7 @@
"test"
],
"build": {
"afterSign": "./scripts/notarize.js",
"appId": "org.zulip.zulip-electron",
"asar": true,
"asarUnpack": [
@@ -57,10 +58,15 @@
"mac": {
"category": "public.app-category.productivity",
"darkModeSupport": true,
"artifactName": "${productName}-${version}-${arch}.${ext}"
"artifactName": "${productName}-${version}-${arch}.${ext}",
"hardenedRuntime": true,
"entitlements": "build/entitlements.mac.plist",
"entitlementsInherit": "build/entitlements.mac.plist",
"gatekeeperAssess": false
},
"linux": {
"category": "Chat;GNOME;GTK;Network;InstantMessaging",
"icon": "build/icon.icns",
"packageCategory": "GNOME;GTK;Network;InstantMessaging",
"description": "Zulip Desktop Client for Linux",
"target": [
@@ -110,6 +116,13 @@
"x64",
"ia32"
]
},
{
"target": "msi",
"arch": [
"x64",
"ia32"
]
}
],
"icon": "build/icon.ico",
@@ -140,9 +153,10 @@
"cp-file": "5.0.0",
"devtron": "1.4.0",
"electron": "3.1.10",
"electron-builder": "20.40.2",
"electron-builder": "20.43.0",
"electron-connect": "0.6.2",
"electron-debug": "1.4.0",
"electron-notarize": "0.2.0",
"eslint-config-xo-typescript": "0.14.0",
"fs-extra": "8.1.0",
"gulp": "4.0.0",

22
scripts/notarize.js Normal file
View File

@@ -0,0 +1,22 @@
const path = require('path');
const dotenv = require('dotenv');
dotenv.config({ path: path.join(__dirname, '/../.env') });
const { notarize } = require('electron-notarize');
exports.default = async function notarizing(context) {
const { electronPlatformName, appOutDir } = context;
if (electronPlatformName !== 'darwin') {
return;
}
const appName = context.packager.appInfo.productFilename;
return await notarize({
appBundleId: 'org.zulip.zulip-electron',
appPath: `${appOutDir}/${appName}.app`,
appleId: process.env.APPLE_ID,
appleIdPassword: process.env.APPLE_ID_PASS,
});
};

1
typings.d.ts vendored
View File

@@ -12,6 +12,7 @@ declare module 'escape-html';
declare module 'fs-extra';
declare module 'wurl';
declare module 'i18n';
declare module 'backoff';
interface PageParamsObject {
realm_uri: string;