Compare commits

...

39 Commits

Author SHA1 Message Date
Akash Nimare
d4d3805be8 release: New release v5.4.2. 2020-08-11 16:09:23 +05:30
Akash Nimare
e853af40c4 electron: Update electron to v9.2.0. 2020-08-11 15:37:13 +05:30
Manav Mehta
941200cf3b changelog: Update changelog for release 5.4.1-beta. 2020-07-29 16:22:11 +05:30
Akash Nimare
cf1f659ebf release: New beta release v5.4.1-beta. 2020-07-29 13:40:59 +05:30
Akash Nimare
eb381a87bc electron-builder: Update builder to latest version. 2020-07-29 01:54:48 +05:30
Manav Mehta
68bc0ae4a0 readme: Add new screenshot URLs.
Update the screenshots to accomodate new Zulip logo and both the day and night modes
2020-07-29 01:31:57 +05:30
Manav Mehta
178bc7f401 macos: Update dock icon.
The icon in macOS was stretched to the boundaries making it larger than the other icons.
A padding of 30px on all sides makes it coherent with the others.

Fixes: #1003.
2020-07-27 01:12:27 +05:30
Anders Kaseorg
0f1245b975 Upgrade dependencies, including Electron 9.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-24 01:37:41 -07:00
Anders Kaseorg
960312a932 notification: Move loadBots call to preload, to break an import cycle.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-24 01:37:07 -07:00
Anders Kaseorg
0e00f3bbce Commit package-lock.json update missed in v5.4.0 release.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-24 00:50:45 -07:00
Anders Kaseorg
ec205f68a6 Send only needed data from tabs over IPC.
Fixes exceptions from the structured clone algorithm raised by
Electron 9.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-24 00:39:38 -07:00
Anders Kaseorg
5fe5989710 xo: Enable import/newline-after-import.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-23 23:18:25 -07:00
Anders Kaseorg
69141b5395 Remove spurious 'use-strict' [sic] directives.
The directive is 'use strict'.  It’s not necessary in TypeScript.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-23 23:09:12 -07:00
Anders Kaseorg
8d66f05924 xo: Sort imports with import/order.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-23 23:06:41 -07:00
Manav Mehta
e7330dbff8 Update changelog for v5.4.0 and the license year to 2020 (#1000)
Co-authored-by: Akash Nimare <akashnimare@users.noreply.github.com>
2020-07-21 22:05:04 +05:30
Akash Nimare
67fa9cca8c release: New release v5.4.0. 2020-07-21 18:44:45 +05:30
Manav Mehta
a90bf1af08 macOS: Change the logo to Z without the circle. 2020-07-21 17:00:07 +05:30
Manav Mehta
cb145acc73 Update icons introducing the new logo. 2020-07-19 01:56:17 +05:30
Akash Nimare
099e10673c translation: Sync strings from Transifex. 2020-07-18 21:28:59 +05:30
Anders Kaseorg
4b3608fc1e dependencies: Upgrade dependencies.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-17 18:57:33 -07:00
Manav Mehta
6128c0e12a appLanguage: add 'sk' and correct some locale names.
* sk was not in supported locales and thus not in dropdown menu
* el-GR is not the correct name and thus not applicable
* zh-Hant/Hans are exact names, though case didn't hamper the translation
2020-07-14 15:51:38 +05:30
Manav Mehta
14a1f5d3e1 request: Replace deprecated request module with net.request.
Co-authored-by: Anders Kaseorg <anders@zulip.com>

Fixes: #886.
2020-07-14 15:50:52 +05:30
Anders Kaseorg
9cf26f4890 dependencies: Upgrade dependencies.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-01 15:01:47 -07:00
Anders Kaseorg
397a7381b8 main: Use import() syntax for electron-connect.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-01 15:01:38 -07:00
Anders Kaseorg
24b28f9ded proxy-util: Fix misuse of void.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-01 15:01:29 -07:00
Anders Kaseorg
9ceabe02d5 Use optional catch binding.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-01 15:01:29 -07:00
Anders Kaseorg
b207ee57de main: Avoid object type.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-01 15:01:25 -07:00
Anders Kaseorg
cf9d0c8aa2 main: Remove mainWindowState from globals.
(No, globals are not accessible from the other process.)

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-07-01 14:47:09 -07:00
Manav Mehta
e97ab2e6dd Replace deprecated getWebContents API 2020-06-30 20:33:41 -07:00
Samyak Gaur
6a7f26d7e8 settings: Update font size for spellchecker explanation.
Fixes: #978.
2020-07-01 03:11:53 +05:30
Anders Kaseorg
b6e11f623a darwin-notifications: Avoid no-useless-return warnings.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-06-26 18:56:07 -07:00
Anders Kaseorg
1c60c335fd logger-util: Avoid no-fallthrough warnings.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-06-26 18:56:03 -07:00
Anders Kaseorg
c9249b1724 context-menu: Avoid no-unnecessary-boolean-literal-compare warning.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-06-26 15:56:23 -07:00
Anders Kaseorg
9e957ba704 context-menu: Copy the email address, not the link text.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-06-26 15:47:38 -07:00
Anders Kaseorg
6c37e30233 Accomodate WebSocket URLs in certificate-error handler.
Our certificates are indexed by https: URLs.  Fixes #988.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-06-26 15:24:55 -07:00
Abhigyan Khaund
addfe2e414 context-menu: Remove trailing and leading separators when not required.
Fixes: #979.
2020-06-27 02:38:03 +05:30
Abhigyan Khaund
bda0dd29df context-menu: Fix bug in Copy Link and add copy Email.
Fixes: #986
2020-06-27 02:27:44 +05:30
Abhigyan Khaund
01926e1234 context-menu: Enable copy only when copy is possible. 2020-06-27 02:27:44 +05:30
Manav Mehta
9138bbfaf2 Update changelog for release v5.3.0. 2020-06-25 00:32:37 +05:30
91 changed files with 2855 additions and 4127 deletions

View File

@@ -6,7 +6,8 @@
Desktop client for Zulip. Available for Mac, Linux, and Windows.
<img src="http://i.imgur.com/ChzTq4F.png"/>
<img src="https://i.imgur.com/s1o6TRA.png"/>
<img src="https://i.imgur.com/vekKnW4.png"/>
# Download
Please see the [installation guide](https://zulip.com/help/desktop-app-install-guide).

View File

@@ -1,20 +1,26 @@
import {app, dialog} from 'electron';
import {UpdateDownloadedEvent, UpdateInfo, autoUpdater} from 'electron-updater';
import {linuxUpdateNotification} from './linuxupdater'; // Required only in case of linux
import {app, dialog, session} from 'electron';
import util from 'util';
import log from 'electron-log';
import isDev from 'electron-is-dev';
import log from 'electron-log';
import {UpdateDownloadedEvent, UpdateInfo, autoUpdater} from 'electron-updater';
import * as ConfigUtil from '../renderer/js/utils/config-util';
import * as LinkUtil from '../renderer/js/utils/link-util';
export function appUpdater(updateFromMenu = false): void {
import {linuxUpdateNotification} from './linuxupdater'; // Required only in case of linux
const sleep = util.promisify(setTimeout);
export async function appUpdater(updateFromMenu = false): Promise<void> {
// Don't initiate auto-updates in development
if (isDev) {
return;
}
if (process.platform === 'linux' && !process.env.APPIMAGE) {
linuxUpdateNotification();
const ses = session.fromPartition('persist:webviewsession');
await linuxUpdateNotification(ses);
return;
}
@@ -94,13 +100,12 @@ export function appUpdater(updateFromMenu = false): void {
detail: 'It will be installed the next time you restart the application'
});
if (response === 0) {
setTimeout(() => {
autoUpdater.quitAndInstall();
// Force app to quit. This is just a workaround, ideally autoUpdater.quitAndInstall() should relaunch the app.
app.quit();
}, 1000);
await sleep(1000);
autoUpdater.quitAndInstall();
// Force app to quit. This is just a workaround, ideally autoUpdater.quitAndInstall() should relaunch the app.
app.quit();
}
});
// Init for updates
(async () => autoUpdater.checkForUpdates())();
await autoUpdater.checkForUpdates();
}

View File

@@ -1,23 +1,22 @@
import {sentryInit} from '../renderer/js/utils/sentry-util';
import {appUpdater} from './autoupdater';
import {setAutoLaunch} from './startup';
import electron, {app, dialog, ipcMain, session} from 'electron';
import fs from 'fs';
import path from 'path';
import windowStateKeeper from 'electron-window-state';
import path from 'path';
import fs from 'fs';
import electron, {app, dialog, ipcMain, session} from 'electron';
import * as AppMenu from './menu';
import * as BadgeSettings from '../renderer/js/pages/preference/badge-settings';
import * as CertificateUtil from '../renderer/js/utils/certificate-util';
import * as ConfigUtil from '../renderer/js/utils/config-util';
import * as ProxyUtil from '../renderer/js/utils/proxy-util';
import {sentryInit} from '../renderer/js/utils/sentry-util';
interface PatchedGlobal extends NodeJS.Global {
mainWindowState: windowStateKeeper.State;
}
import {appUpdater} from './autoupdater';
import * as AppMenu from './menu';
import {_getServerSettings, _saveServerIcon, _isOnline} from './request';
import {setAutoLaunch} from './startup';
const globalPatched = global as PatchedGlobal;
let mainWindowState: windowStateKeeper.State;
// Prevent window being garbage collected
let mainWindow: Electron.BrowserWindow;
@@ -77,9 +76,6 @@ function createMainWindow(): Electron.BrowserWindow {
path: `${app.getPath('userData')}/config`
});
// Let's keep the window position global so that we can access it in other process
globalPatched.mainWindowState = mainWindowState;
const win = new electron.BrowserWindow({
// This settings needs to be saved in config
title: 'Zulip',
@@ -167,6 +163,7 @@ app.on('activate', () => {
app.on('ready', () => {
const ses = session.fromPartition('persist:webviewsession');
ses.setUserAgent(`ZulipElectron/${app.getVersion()} ${ses.getUserAgent()}`);
ipcMain.on('set-spellcheck-langs', () => {
ses.setSpellCheckerLanguages(ConfigUtil.getConfigItem('spellcheckerLanguages'));
});
@@ -208,17 +205,23 @@ app.on('ready', () => {
event.returnValue = session.fromPartition('persist:webviewsession').getUserAgent();
});
page.once('did-frame-finish-load', () => {
ipcMain.handle('get-server-settings', async (event, domain: string) => _getServerSettings(domain, ses));
ipcMain.handle('save-server-icon', async (event, url: string) => _saveServerIcon(url, ses));
ipcMain.handle('is-online', async (event, url: string) => _isOnline(url, ses));
page.once('did-frame-finish-load', async () => {
// Initiate auto-updates on MacOS and Windows
if (ConfigUtil.getConfigItem('autoUpdate')) {
appUpdater();
await appUpdater();
}
});
app.on('certificate-error', (
event: Event,
webContents: Electron.WebContents,
url: string,
urlString: string,
error: string,
certificate: Electron.Certificate,
callback: (isTrusted: boolean) => void
@@ -226,8 +229,12 @@ app.on('ready', () => {
// TODO: The entire concept of selectively ignoring certificate errors
// is ill-conceived, and this handler needs to be deleted.
const {origin} = new URL(url);
const filename = CertificateUtil.getCertificate(encodeURIComponent(origin));
const url = new URL(urlString);
if (url.protocol === 'wss:') {
url.protocol = 'https:';
}
const filename = CertificateUtil.getCertificate(encodeURIComponent(url.origin));
if (filename !== undefined) {
try {
const savedCertificate = fs.readFileSync(
@@ -247,7 +254,7 @@ app.on('ready', () => {
dialog.showErrorBox(
'Certificate error',
`The server presented an invalid certificate for ${origin}:
`The server presented an invalid certificate for ${url.origin}:
${error}`
);
@@ -311,7 +318,7 @@ ${error}`
});
ipcMain.on('clear-app-settings', () => {
globalPatched.mainWindowState.unmanage();
mainWindowState.unmanage();
app.relaunch();
app.exit();
});
@@ -348,7 +355,7 @@ ${error}`
AppMenu.setMenu(props);
const activeTab = props.tabs[props.activeTabIndex];
if (activeTab) {
mainWindow.setTitle(`Zulip - ${activeTab.webview.props.name}`);
mainWindow.setTitle(`Zulip - ${activeTab.webviewName}`);
}
});
@@ -360,7 +367,7 @@ ${error}`
page.downloadURL(url);
page.session.once('will-download', async (_event: Event, item) => {
if (ConfigUtil.getConfigItem('promptDownload', false)) {
const showDialogOptions: object = {
const showDialogOptions: electron.SaveDialogOptions = {
defaultPath: path.join(downloadPath, item.getFilename())
};
item.setSaveDialogOptions(showDialogOptions);

View File

@@ -1,52 +1,45 @@
import {app, Notification} from 'electron';
import {app, Notification, net} from 'electron';
import request from 'request';
import getStream from 'get-stream';
import semver from 'semver';
import * as ConfigUtil from '../renderer/js/utils/config-util';
import * as ProxyUtil from '../renderer/js/utils/proxy-util';
import * as LinuxUpdateUtil from '../renderer/js/utils/linux-update-util';
import Logger from '../renderer/js/utils/logger-util';
import {fetchResponse} from './request';
const logger = new Logger({
file: 'linux-update-util.log',
timestamp: true
});
export function linuxUpdateNotification(): void {
export async function linuxUpdateNotification(session: Electron.session): Promise<void> {
let url = 'https://api.github.com/repos/zulip/zulip-desktop/releases';
url = ConfigUtil.getConfigItem('betaUpdate') ? url : url + '/latest';
const proxyEnabled = ConfigUtil.getConfigItem('useManualProxy') || ConfigUtil.getConfigItem('useSystemProxy');
const options = {
url,
headers: {'User-Agent': 'request'},
proxy: proxyEnabled ? ProxyUtil.getProxy(url) : '',
ecdhCurve: 'auto'
};
request(options, (error, response: request.Response, body: string) => {
if (error) {
logger.error('Linux update error.');
logger.error(error);
try {
const response = await fetchResponse(net.request({url, session}));
if (response.statusCode !== 200) {
logger.log('Linux update response status: ', response.statusCode);
return;
}
if (response.statusCode < 400) {
const data = JSON.parse(body);
const latestVersion = ConfigUtil.getConfigItem('betaUpdate') ? data[0].tag_name : data.tag_name;
if (typeof latestVersion !== 'string') {
throw new TypeError('Expected string for tag_name');
}
if (semver.gt(latestVersion, app.getVersion())) {
const notified = LinuxUpdateUtil.getUpdateItem(latestVersion);
if (notified === null) {
new Notification({title: 'Zulip Update', body: `A new version ${latestVersion} is available. Please update using your package manager.`}).show();
LinuxUpdateUtil.setUpdateItem(latestVersion, true);
}
}
} else {
logger.log('Linux update response status: ', response.statusCode);
const data = JSON.parse(await getStream(response));
const latestVersion = ConfigUtil.getConfigItem('betaUpdate') ? data[0].tag_name : data.tag_name;
if (typeof latestVersion !== 'string') {
throw new TypeError('Expected string for tag_name');
}
});
if (semver.gt(latestVersion, app.getVersion())) {
const notified = LinuxUpdateUtil.getUpdateItem(latestVersion);
if (notified === null) {
new Notification({title: 'Zulip Update', body: `A new version ${latestVersion} is available. Please update using your package manager.`}).show();
LinuxUpdateUtil.setUpdateItem(latestVersion, true);
}
}
} catch (error) {
logger.error('Linux update error.');
logger.error(error);
}
}

View File

@@ -1,15 +1,17 @@
import {app, shell, BrowserWindow, Menu} from 'electron';
import {appUpdater} from './autoupdater';
import AdmZip from 'adm-zip';
import * as DNDUtil from '../renderer/js/utils/dnd-util';
import type {TabData} from '../renderer/js/main';
import * as ConfigUtil from '../renderer/js/utils/config-util';
import * as DNDUtil from '../renderer/js/utils/dnd-util';
import * as LinkUtil from '../renderer/js/utils/link-util';
import * as t from '../renderer/js/utils/translation-util';
import type {ServerOrFunctionalTab} from '../renderer/js/main';
import {appUpdater} from './autoupdater';
export interface MenuProps {
tabs: ServerOrFunctionalTab[];
tabs: TabData[];
activeTabIndex?: number;
enableMenu?: boolean;
}
@@ -41,8 +43,8 @@ function getHistorySubmenu(enableMenu: boolean): Electron.MenuItemConstructorOpt
function getToolsSubmenu(): Electron.MenuItemConstructorOptions[] {
return [{
label: t.__('Check for Updates'),
click() {
checkForUpdate();
async click() {
await checkForUpdate();
}
},
{
@@ -213,7 +215,7 @@ function getHelpSubmenu(): Electron.MenuItemConstructorOptions[] {
];
}
function getWindowSubmenu(tabs: ServerOrFunctionalTab[], activeTabIndex: number): Electron.MenuItemConstructorOptions[] {
function getWindowSubmenu(tabs: TabData[], activeTabIndex: number): Electron.MenuItemConstructorOptions[] {
const initialSubmenu: Electron.MenuItemConstructorOptions[] = [{
label: t.__('Minimize'),
role: 'minimize'
@@ -229,17 +231,17 @@ function getWindowSubmenu(tabs: ServerOrFunctionalTab[], activeTabIndex: number)
});
tabs.forEach(tab => {
// Do not add functional tab settings to list of windows in menu bar
if (tab.props.role === 'function' && tab.props.name === 'Settings') {
if (tab.role === 'function' && tab.name === 'Settings') {
return;
}
initialSubmenu.push({
label: tab.props.name,
accelerator: tab.props.role === 'function' ? '' : `${ShortcutKey} + ${tab.props.index + 1}`,
checked: tab.props.index === activeTabIndex,
label: tab.name,
accelerator: tab.role === 'function' ? '' : `${ShortcutKey} + ${tab.index + 1}`,
checked: tab.index === activeTabIndex,
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction('switch-server-tab', tab.props.index);
sendAction('switch-server-tab', tab.index);
}
},
type: 'checkbox'
@@ -541,24 +543,24 @@ function sendAction(action: string, ...parameters: unknown[]): void {
win.webContents.send(action, ...parameters);
}
function checkForUpdate(): void {
appUpdater(true);
async function checkForUpdate(): Promise<void> {
await appUpdater(true);
}
function getNextServer(tabs: ServerOrFunctionalTab[], activeTabIndex: number): number {
function getNextServer(tabs: TabData[], activeTabIndex: number): number {
do {
activeTabIndex = (activeTabIndex + 1) % tabs.length;
}
while (tabs[activeTabIndex].props.role !== 'server');
while (tabs[activeTabIndex].role !== 'server');
return activeTabIndex;
}
function getPreviousServer(tabs: ServerOrFunctionalTab[], activeTabIndex: number): number {
function getPreviousServer(tabs: TabData[], activeTabIndex: number): number {
do {
activeTabIndex = (activeTabIndex - 1 + tabs.length) % tabs.length;
}
while (tabs[activeTabIndex].props.role !== 'server');
while (tabs[activeTabIndex].role !== 'server');
return activeTabIndex;
}

113
app/main/request.ts Normal file
View File

@@ -0,0 +1,113 @@
import {ClientRequest, IncomingMessage, app, net} from 'electron';
import fs from 'fs';
import path from 'path';
import stream from 'stream';
import util from 'util';
import escape from 'escape-html';
import getStream from 'get-stream';
import {ServerConf} from '../renderer/js/utils/domain-util';
import Logger from '../renderer/js/utils/logger-util';
import * as Messages from '../resources/messages';
export async function fetchResponse(request: ClientRequest): Promise<IncomingMessage> {
return new Promise((resolve, reject) => {
request.on('response', resolve);
request.on('abort', () => reject(new Error('Request aborted')));
request.on('error', reject);
request.end();
});
}
const pipeline = util.promisify(stream.pipeline);
/* Request: domain-util */
const defaultIconUrl = '../renderer/img/icon.png';
const logger = new Logger({
file: 'domain-util.log',
timestamp: true
});
const generateFilePath = (url: string): string => {
const dir = `${app.getPath('userData')}/server-icons`;
const extension = path.extname(url).split('?')[0];
let hash = 5381;
let {length} = url;
while (length) {
hash = (hash * 33) ^ url.charCodeAt(--length);
}
// Create 'server-icons' directory if not existed
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
return `${dir}/${hash >>> 0}${extension}`;
};
export const _getServerSettings = async (domain: string, session: Electron.session): Promise<ServerConf> => {
const response = await fetchResponse(net.request({
url: domain + '/api/v1/server_settings',
session
}));
if (response.statusCode !== 200) {
throw new Error(Messages.invalidZulipServerError(domain));
}
const {realm_name, realm_uri, realm_icon} = JSON.parse(await getStream(response));
if (
typeof realm_name !== 'string' ||
typeof realm_uri !== 'string' ||
typeof realm_icon !== 'string'
) {
throw new TypeError(Messages.noOrgsError(domain));
}
return {
// Some Zulip Servers use absolute URL for server icon whereas others use relative URL
// Following check handles both the cases
icon: realm_icon.startsWith('/') ? realm_uri + realm_icon : realm_icon,
url: realm_uri,
alias: escape(realm_name)
};
};
export const _saveServerIcon = async (url: string, session: Electron.session): Promise<string> => {
try {
const response = await fetchResponse(net.request({url, session}));
if (response.statusCode !== 200) {
logger.log('Could not get server icon.');
return defaultIconUrl;
}
const filePath = generateFilePath(url);
await pipeline(response, fs.createWriteStream(filePath));
return filePath;
} catch (error) {
logger.log('Could not get server icon.');
logger.log(error);
logger.reportSentry(error);
return defaultIconUrl;
}
};
/* Request: reconnect-util */
export const _isOnline = async (url: string, session: Electron.session): Promise<boolean> => {
try {
const response = await fetchResponse(net.request({
url: `${url}/static/favicon.ico`,
session
}));
const isValidResponse = response.statusCode >= 200 && response.statusCode < 400;
return isValidResponse;
} catch (error) {
logger.log(error);
return false;
}
};

View File

@@ -2,6 +2,7 @@ import {app} from 'electron';
import AutoLaunch from 'auto-launch';
import isDev from 'electron-is-dev';
import * as ConfigUtil from '../renderer/js/utils/config-util';
export const setAutoLaunch = async (AutoLaunchValue: boolean): Promise<void> => {

View File

@@ -389,6 +389,10 @@ img.server-info-icon {
margin: 6px;
}
#note {
font-size: 10px;
}
.code {
font-family: Courier New, Courier, monospace;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -1,5 +1,5 @@
import {clipboard} from 'electron';
import crypto from 'crypto';
import {clipboard} from 'electron';
// This helper is exposed via electron_bridge for use in the social
// login flow.
@@ -59,7 +59,7 @@ export class ClipboardDecrypterImpl implements ClipboardDecrypter {
plaintext =
decipher.update(ciphertext, undefined, 'utf8') +
decipher.final('utf8');
} catch (_) {
} catch {
// If the parsing or decryption failed in any way,
// the correct token hasnt been copied yet; try
// again next time.

View File

@@ -1,10 +1,14 @@
import {remote, ContextMenuParams} from 'electron';
import * as t from '../utils/translation-util';
const {clipboard, Menu} = remote;
export const contextMenu = (webContents: Electron.WebContents, event: Event, props: ContextMenuParams) => {
const isText = Boolean(props.selectionText.length);
const isLink = Boolean(props.linkURL);
const isText = props.selectionText !== '';
const isLink = props.linkURL !== '';
const linkURL = isLink ? new URL(props.linkURL) : undefined;
const isEmailAddress = isLink ? linkURL.protocol === 'mailto:' : undefined;
const makeSuggestion = (suggestion: string) => ({
label: suggestion,
@@ -21,7 +25,8 @@ export const contextMenu = (webContents: Electron.WebContents, event: Event, pro
webContents.session.addWordToSpellCheckerDictionary(props.misspelledWord);
}
}, {
type: 'separator'
type: 'separator',
visible: props.isEditable && isText && props.misspelledWord.length !== 0
}, {
label: `${t.__('Look Up')} "${props.selectionText}"`,
visible: process.platform === 'darwin' && isText,
@@ -29,7 +34,8 @@ export const contextMenu = (webContents: Electron.WebContents, event: Event, pro
webContents.showDefinitionForSelection();
}
}, {
type: 'separator'
type: 'separator',
visible: process.platform === 'darwin' && isText
}, {
label: t.__('Cut'),
visible: isText,
@@ -41,6 +47,7 @@ export const contextMenu = (webContents: Electron.WebContents, event: Event, pro
}, {
label: t.__('Copy'),
accelerator: 'CommandOrControl+C',
enabled: props.editFlags.canCopy,
click(_item) {
webContents.copy();
}
@@ -54,12 +61,12 @@ export const contextMenu = (webContents: Electron.WebContents, event: Event, pro
}, {
type: 'separator'
}, {
label: t.__('Copy Link'),
visible: isText && isLink,
label: isEmailAddress ? t.__('Copy Email Address') : t.__('Copy Link'),
visible: isLink,
click(_item) {
clipboard.write({
bookmark: props.linkText,
text: props.linkURL
text: isEmailAddress ? linkURL.pathname : props.linkURL
});
}
}, {
@@ -78,7 +85,8 @@ export const contextMenu = (webContents: Electron.WebContents, event: Event, pro
});
}
}, {
type: 'separator'
type: 'separator',
visible: isLink || props.mediaType === 'image'
}, {
label: t.__('Services'),
visible: process.platform === 'darwin',
@@ -96,7 +104,12 @@ export const contextMenu = (webContents: Electron.WebContents, event: Event, pro
});
}
}
// Hide the invisible separators on Linux and Windows
// Electron has a bug which ignores visible: false parameter for separator menuitems. So we remove them here.
// https://github.com/electron/electron/issues/5869
// https://github.com/electron/electron/issues/6906
const menu = Menu.buildFromTemplate(menuTemplate);
const filteredMenuTemplate = menuTemplate.filter(menuItem => menuItem.visible ?? true);
const menu = Menu.buildFromTemplate(filteredMenuTemplate);
menu.popup();
};

View File

@@ -1,7 +1,8 @@
import {ipcRenderer, remote} from 'electron';
import * as LinkUtil from '../utils/link-util';
import * as ConfigUtil from '../utils/config-util';
import * as LinkUtil from '../utils/link-util';
import type WebView from './webview';
const {shell, app} = remote;

View File

@@ -1,8 +1,9 @@
import {ipcRenderer} from 'electron';
import Tab, {TabProps} from './tab';
import * as SystemUtil from '../utils/system-util';
import Tab, {TabProps} from './tab';
export default class ServerTab extends Tab {
$badge: Element;

View File

@@ -1,5 +1,5 @@
import WebView from './webview';
import BaseComponent from './base';
import WebView from './webview';
export interface TabProps {
role: string;

View File

@@ -1,12 +1,13 @@
import {ipcRenderer, remote} from 'electron';
import path from 'path';
import fs from 'fs';
import path from 'path';
import * as ConfigUtil from '../utils/config-util';
import * as SystemUtil from '../utils/system-util';
import BaseComponent from './base';
import handleExternalLink from './handle-external-link';
import {contextMenu} from './context-menu';
import handleExternalLink from './handle-external-link';
const {app, dialog} = remote;
@@ -216,7 +217,7 @@ export default class WebView extends BaseComponent {
focus(): void {
// Focus Webview and it's contents when Window regain focus.
const webContents = this.$el.getWebContents();
const webContents = remote.webContents.fromId(this.$el.getWebContentsId());
// HACK: webContents.isFocused() seems to be true even without the element
// being in focus. So, we check against `document.activeElement`.
if (webContents && this.$el !== document.activeElement) {

View File

@@ -1,5 +1,4 @@
import {ipcRenderer} from 'electron';
import {EventEmitter} from 'events';
import {ClipboardDecrypterImpl} from './clipboard-decrypter';

View File

@@ -1,8 +1,8 @@
import {remote} from 'electron';
import SendFeedback from '@electron-elements/send-feedback';
import path from 'path';
import fs from 'fs';
import path from 'path';
import SendFeedback from '@electron-elements/send-feedback';
const {app} = remote;

View File

@@ -1,27 +1,29 @@
import {ipcRenderer, remote, clipboard} from 'electron';
import {feedbackHolder} from './feedback';
import path from 'path';
import escape from 'escape-html';
import isDev from 'electron-is-dev';
const {session, app, Menu, dialog} = remote;
import escape from 'escape-html';
import * as Messages from '../../resources/messages';
import FunctionalTab from './components/functional-tab';
import ServerTab from './components/server-tab';
import WebView from './components/webview';
import {feedbackHolder} from './feedback';
import * as CommonUtil from './utils/common-util';
import * as ConfigUtil from './utils/config-util';
import * as DNDUtil from './utils/dnd-util';
import type {DNDSettings} from './utils/dnd-util';
import * as DomainUtil from './utils/domain-util';
import * as EnterpriseUtil from './utils/enterprise-util';
import * as LinkUtil from './utils/link-util';
import Logger from './utils/logger-util';
import ReconnectUtil from './utils/reconnect-util';
// eslint-disable-next-line import/no-unassigned-import
import './tray';
import * as DomainUtil from './utils/domain-util';
import WebView from './components/webview';
import ServerTab from './components/server-tab';
import FunctionalTab from './components/functional-tab';
import * as ConfigUtil from './utils/config-util';
import * as DNDUtil from './utils/dnd-util';
import ReconnectUtil from './utils/reconnect-util';
import Logger from './utils/logger-util';
import * as CommonUtil from './utils/common-util';
import * as EnterpriseUtil from './utils/enterprise-util';
import * as LinkUtil from './utils/link-util';
import * as Messages from '../../resources/messages';
import type {DNDSettings} from './utils/dnd-util';
const {session, app, Menu, dialog} = remote;
interface FunctionalTabProps {
name: string;
@@ -59,7 +61,14 @@ const logger = new Logger({
});
const rendererDirectory = path.resolve(__dirname, '..');
export type ServerOrFunctionalTab = ServerTab | FunctionalTab;
type ServerOrFunctionalTab = ServerTab | FunctionalTab;
export interface TabData {
role: string;
name: string;
index: number;
webviewName: string;
}
class ServerManagerView {
$addServerButton: HTMLButtonElement;
@@ -590,19 +599,13 @@ class ServerManagerView {
// not crash app when this.tabs is passed into
// ipcRenderer. Something about webview, and props.webview
// properties in ServerTab causes the app to crash.
get tabsForIpc(): ServerOrFunctionalTab[] {
const tabs: ServerOrFunctionalTab[] = [];
this.tabs.forEach((tab: ServerOrFunctionalTab) => {
const proto = Object.create(Object.getPrototypeOf(tab));
const tabClone = Object.assign(proto, tab);
tabClone.webview = {props: {}};
tabClone.webview.props.name = tab.webview.props.name;
delete tabClone.props.webview;
tabs.push(tabClone);
});
return tabs;
get tabsForIpc(): TabData[] {
return this.tabs.map(tab => ({
role: tab.props.role,
name: tab.props.name,
index: tab.props.index,
webviewName: tab.webview.props.name
}));
}
activateTab(index: number, hideOldTab = true): void {
@@ -627,7 +630,7 @@ class ServerManagerView {
try {
this.tabs[index].webview.canGoBackButton();
} catch (_) {
} catch {
}
this.activeTabIndex = index;
@@ -717,7 +720,7 @@ class ServerManagerView {
updateGeneralSettings(setting: string, value: unknown): void {
if (this.getActiveWebview()) {
const webContents = this.getActiveWebview().getWebContents();
const webContents = remote.webContents.fromId(this.getActiveWebview().getWebContentsId());
webContents.send(setting, value);
}
}
@@ -893,7 +896,7 @@ class ServerManagerView {
webviews.forEach(webview => {
try {
webview.setAudioMuted(state);
} catch (_) {
} catch {
// Webview is not ready yet
webview.addEventListener('dom-ready', () => {
webview.setAudioMuted(state);
@@ -917,7 +920,7 @@ class ServerManagerView {
ipcRenderer.on('toggle-dnd', (event: Event, state: boolean, newSettings: DNDSettings) => {
this.toggleDNDButton(state);
ipcRenderer.send('forward-message', 'toggle-silent', newSettings.silent);
const webContents = this.getActiveWebview().getWebContents();
const webContents = remote.webContents.fromId(this.getActiveWebview().getWebContentsId());
webContents.send('toggle-dnd', state, newSettings);
});
@@ -969,7 +972,7 @@ class ServerManagerView {
ipcRenderer.on('focus-webview-with-id', (event: Event, webviewId: number) => {
const webviews: NodeListOf<Electron.WebviewTag> = document.querySelectorAll('webview');
webviews.forEach(webview => {
const currentId = webview.getWebContents().id;
const currentId = webview.getWebContentsId();
const tabId = webview.getAttribute('data-tab-id');
const concurrentTab: HTMLButtonElement = document.querySelector(`div[data-tab-id="${tabId}"]`);
if (currentId === webviewId) {
@@ -1046,7 +1049,8 @@ window.addEventListener('load', async () => {
// Only start electron-connect (auto reload on change) when its ran
// from `npm run dev` or `gulp dev` and not from `npm start`
if (isDev && remote.getGlobal('process').argv.includes('--electron-connect')) {
require('electron-connect').client.create();
// eslint-disable-next-line node/no-unsupported-features/es-syntax
(await import('electron-connect')).client.create();
}
const serverManagerView = new ServerManagerView();

View File

@@ -1,12 +1,14 @@
import {ipcRenderer} from 'electron';
import MacNotifier from 'node-mac-notifier';
import electron_bridge from '../electron-bridge';
import * as ConfigUtil from '../utils/config-util';
import {
appId, customReply, focusCurrentServer, parseReply
} from './helpers';
import MacNotifier from 'node-mac-notifier';
import * as ConfigUtil from '../utils/config-util';
import electron_bridge from '../electron-bridge';
type ReplyHandler = (response: string) => void;
type ClickHandler = () => void;
let replyHandler: ReplyHandler;
@@ -47,7 +49,7 @@ class DarwinNotification {
}
static requestPermission(): void {
return; // eslint-disable-line no-useless-return
// Do nothing
}
// Override default Notification permission
@@ -104,7 +106,7 @@ class DarwinNotification {
// Method specific to notification api
// used by zulip
close(): void {
return; // eslint-disable-line no-useless-return
// Do nothing
}
}

View File

@@ -1,8 +1,9 @@
import {ipcRenderer} from 'electron';
import {focusCurrentServer} from './helpers';
import * as ConfigUtil from '../utils/config-util';
import {focusCurrentServer} from './helpers';
const NativeNotification = window.Notification;
export default class BaseNotification extends NativeNotification {
constructor(title: string, options: NotificationOptions) {

View File

@@ -1,8 +1,8 @@
import {remote} from 'electron';
import electron_bridge from '../electron-bridge';
import {appId, loadBots} from './helpers';
import DefaultNotification from './default-notification';
import {appId} from './helpers';
const {app} = remote;
// From https://github.com/felixrieseberg/electron-windows-notifications#appusermodelid
@@ -67,7 +67,3 @@ export function newNotification(
actions: notification.actions
};
}
electron_bridge.once('zulip-loaded', async () => {
await loadBots();
});

View File

@@ -1,8 +1,6 @@
'use-strict';
import {remote, OpenDialogOptions} from 'electron';
import path from 'path';
import BaseComponent from '../../components/base';
import * as CertificateUtil from '../../utils/certificate-util';
import * as DomainUtil from '../../utils/domain-util';

View File

@@ -1,4 +1,5 @@
import {ipcRenderer} from 'electron';
import escape from 'escape-html';
import BaseComponent from '../../components/base';

View File

@@ -1,12 +1,13 @@
import {ipcRenderer} from 'electron';
import BaseSection from './base-section';
import * as DomainUtil from '../../utils/domain-util';
import ServerInfoForm from './server-info-form';
import AddCertificate from './add-certificate';
import FindAccounts from './find-accounts';
import * as t from '../../utils/translation-util';
import AddCertificate from './add-certificate';
import BaseSection from './base-section';
import FindAccounts from './find-accounts';
import ServerInfoForm from './server-info-form';
interface ConnectedOrgSectionProps {
$root: Element;
}

View File

@@ -1,5 +1,3 @@
'use-strict';
import BaseComponent from '../../components/base';
import * as LinkUtil from '../../utils/link-util';
import * as t from '../../utils/translation-util';

View File

@@ -1,18 +1,19 @@
import {ipcRenderer, remote, OpenDialogOptions} from 'electron';
import path from 'path';
import Tagify from '@yaireo/tagify';
import fs from 'fs-extra';
import ISO6391 from 'iso-639-1';
const {app, dialog, session} = remote;
const currentBrowserWindow = remote.getCurrentWindow();
import BaseSection from './base-section';
import supportedLocales from '../../../../translations/supported-locales.json';
import * as ConfigUtil from '../../utils/config-util';
import * as EnterpriseUtil from '../../utils/enterprise-util';
import * as t from '../../utils/translation-util';
import supportedLocales from '../../../../translations/supported-locales.json';
import Tagify from '@yaireo/tagify';
import ISO6391 from 'iso-639-1';
import BaseSection from './base-section';
const {app, dialog, session} = remote;
const currentBrowserWindow = remote.getCurrentWindow();
interface GeneralSectionProps {
$root: Element;

View File

@@ -1,9 +1,10 @@
import {ipcRenderer} from 'electron';
import BaseSection from './base-section';
import * as ConfigUtil from '../../utils/config-util';
import * as t from '../../utils/translation-util';
import BaseSection from './base-section';
interface NetworkSectionProps {
$root: Element;
}

View File

@@ -1,14 +1,15 @@
import {ipcRenderer} from 'electron';
import BaseComponent from '../../components/base';
import Nav from './nav';
import ServersSection from './servers-section';
import GeneralSection from './general-section';
import NetworkSection from './network-section';
import ConnectedOrgSection from './connected-org-section';
import ShortcutsSection from './shortcuts-section';
import type {DNDSettings} from '../../utils/dnd-util';
import ConnectedOrgSection from './connected-org-section';
import GeneralSection from './general-section';
import Nav from './nav';
import NetworkSection from './network-section';
import ServersSection from './servers-section';
import ShortcutsSection from './shortcuts-section';
type Section = ServersSection | GeneralSection | NetworkSection | ConnectedOrgSection | ShortcutsSection;
export default class PreferenceView extends BaseComponent {

View File

@@ -1,8 +1,8 @@
import {remote, ipcRenderer} from 'electron';
import * as Messages from '../../../../resources/messages';
import BaseComponent from '../../components/base';
import * as DomainUtil from '../../utils/domain-util';
import * as Messages from '../../../../resources/messages';
import * as t from '../../utils/translation-util';
const {dialog} = remote;

View File

@@ -1,6 +1,7 @@
import * as t from '../../utils/translation-util';
import BaseSection from './base-section';
import NewServerForm from './new-server-form';
import * as t from '../../utils/translation-util';
interface ServersSectionProps {
$root: Element;

View File

@@ -1,7 +1,8 @@
import BaseSection from './base-section';
import * as LinkUtil from '../../utils/link-util';
import * as t from '../../utils/translation-util';
import BaseSection from './base-section';
interface ShortcutsSectionProps {
$root: Element;
}

View File

@@ -3,18 +3,20 @@ import fs from 'fs';
import isDev from 'electron-is-dev';
import electron_bridge from './electron-bridge';
import {loadBots} from './notification/helpers';
import * as NetworkError from './pages/network';
// eslint-disable-next-line import/no-unassigned-import
import './notification';
// Prevent drag and drop event in main process which prevents remote code executaion
// eslint-disable-next-line import/no-unassigned-import
import './shared/preventdrag';
import electron_bridge from './electron-bridge';
contextBridge.exposeInMainWorld('raw_electron_bridge', electron_bridge);
electron_bridge.once('zulip-loaded', async () => {
await loadBots();
});
ipcRenderer.on('logout', () => {
// Create the menu for the below
const dropdown: HTMLElement = document.querySelector('.dropdown-toggle');

View File

@@ -1,6 +1,6 @@
import {ipcRenderer, remote, WebviewTag, NativeImage} from 'electron';
import path from 'path';
import * as ConfigUtil from './utils/config-util';
const {Tray, Menu, nativeImage, BrowserWindow} = remote;
@@ -207,7 +207,7 @@ function toggleTray(): void {
const selector = 'webview:not([class*=disabled])';
const webview: WebviewTag = document.querySelector(selector);
const webContents = webview.getWebContents();
const webContents = remote.webContents.fromId(webview.getWebContentsId());
webContents.send('toggletray', state);
}

View File

@@ -1,9 +1,10 @@
import electron from 'electron';
import {JsonDB} from 'node-json-db';
import {initSetUp} from './default-util';
import fs from 'fs';
import path from 'path';
import {JsonDB} from 'node-json-db';
import {initSetUp} from './default-util';
import Logger from './logger-util';
const {app, dialog} =

View File

@@ -1,10 +1,11 @@
import {JsonDB} from 'node-json-db';
import electron from 'electron';
import fs from 'fs';
import path from 'path';
import electron from 'electron';
import Logger from './logger-util';
import {JsonDB} from 'node-json-db';
import * as EnterpriseUtil from './enterprise-util';
import Logger from './logger-util';
const logger = new Logger({
file: 'config-util.log',

View File

@@ -1,16 +1,14 @@
import {JsonDB} from 'node-json-db';
import escape from 'escape-html';
import request from 'request';
import {remote, ipcRenderer} from 'electron';
import fs from 'fs';
import path from 'path';
import Logger from './logger-util';
import {remote} from 'electron';
import * as RequestUtil from './request-util';
import * as EnterpriseUtil from './enterprise-util';
import {JsonDB} from 'node-json-db';
import * as Messages from '../../../resources/messages';
import * as EnterpriseUtil from './enterprise-util';
import Logger from './logger-util';
const {app, dialog} = remote;
export interface ServerConf {
@@ -146,74 +144,11 @@ export async function checkDomain(domain: string, silent = false): Promise<Serve
}
async function getServerSettings(domain: string): Promise<ServerConf> {
const serverSettingsOptions = {
url: domain + '/api/v1/server_settings',
...RequestUtil.requestOptions(domain)
};
return new Promise((resolve, reject) => {
request(serverSettingsOptions, (error: Error, response: request.Response) => {
if (!error && response.statusCode === 200) {
const {realm_name, realm_uri, realm_icon} = JSON.parse(response.body);
if (
typeof realm_name === 'string' &&
typeof realm_uri === 'string' &&
typeof realm_icon === 'string'
) {
resolve({
// Some Zulip Servers use absolute URL for server icon whereas others use relative URL
// Following check handles both the cases
icon: realm_icon.startsWith('/') ? realm_uri + realm_icon : realm_icon,
url: realm_uri,
alias: escape(realm_name)
});
} else {
reject(Messages.noOrgsError(domain));
}
} else {
reject(response);
}
});
});
return ipcRenderer.invoke('get-server-settings', domain);
}
export async function saveServerIcon(server: ServerConf): Promise<string> {
const url = server.icon;
const domain = server.url;
const serverIconOptions = {
url,
...RequestUtil.requestOptions(domain)
};
// The save will always succeed. If url is invalid, downgrade to default icon.
return new Promise(resolve => {
const filePath = generateFilePath(url);
const file = fs.createWriteStream(filePath);
try {
request(serverIconOptions).on('response', (response: request.Response) => {
response.on('error', (err: Error) => {
logger.log('Could not get server icon.');
logger.log(err);
logger.reportSentry(err);
resolve(defaultIconUrl);
});
response.pipe(file).on('finish', () => {
resolve(filePath);
});
}).on('error', (err: Error) => {
logger.log('Could not get server icon.');
logger.log(err);
logger.reportSentry(err);
resolve(defaultIconUrl);
});
} catch (error) {
logger.log('Could not get server icon.');
logger.log(error);
logger.reportSentry(error);
resolve(defaultIconUrl);
}
});
return ipcRenderer.invoke('save-server-icon', server.icon);
}
export async function updateSavedServer(url: string, index: number): Promise<void> {
@@ -256,25 +191,6 @@ function reloadDB(): void {
db = new JsonDB(domainJsonPath, true, true);
}
function generateFilePath(url: string): string {
const dir = `${app.getPath('userData')}/server-icons`;
const extension = path.extname(url).split('?')[0];
let hash = 5381;
let {length} = url;
while (length) {
hash = (hash * 33) ^ url.charCodeAt(--length);
}
// Create 'server-icons' directory if not existed
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
return `${dir}/${hash >>> 0}${extension}`;
}
export function formatUrl(domain: string): string {
if (domain.startsWith('http://') || domain.startsWith('https://')) {
return domain;

View File

@@ -1,9 +1,10 @@
import {shell} from 'electron';
import escape from 'escape-html';
import fs from 'fs';
import os from 'os';
import path from 'path';
import escape from 'escape-html';
export function isUploadsUrl(server: string, url: URL): boolean {
return url.origin === server && url.pathname.startsWith('/user_uploads/');
}
@@ -36,7 +37,7 @@ export async function openBrowser(url: URL): Promise<void> {
</body>
</html>
`);
shell.openItem(file);
await shell.openPath(file);
setTimeout(() => {
fs.unlinkSync(file);
fs.rmdirSync(dir);

View File

@@ -1,8 +1,9 @@
import {JsonDB} from 'node-json-db';
import electron from 'electron';
import fs from 'fs';
import path from 'path';
import electron from 'electron';
import {JsonDB} from 'node-json-db';
import Logger from './logger-util';
const remote =

View File

@@ -1,11 +1,12 @@
import {Console} from 'console'; // eslint-disable-line node/prefer-global/console
import {initSetUp} from './default-util';
import {sentryInit, captureException} from './sentry-util';
import electron from 'electron';
import fs from 'fs';
import os from 'os';
import isDev from 'electron-is-dev';
import electron from 'electron';
import {initSetUp} from './default-util';
import {sentryInit, captureException} from './sentry-util';
interface LoggerOptions {
timestamp?: true | (() => string);
@@ -89,20 +90,21 @@ export default class Logger {
nodeConsole, timestamp, level, logInDevMode
} = this;
/* eslint-disable no-fallthrough */
switch (true) {
case typeof timestamp === 'function':
args.unshift(timestamp() + ' |\t');
// Fall through
case (level):
args.unshift(type.toUpperCase() + ' |');
// Fall through
case isDev || logInDevMode:
nodeConsole[type](...args);
break;
default: break;
default:
}
/* eslint-enable no-fallthrough */
console[type](...args);
}

View File

@@ -5,45 +5,6 @@ export interface ProxyRule {
port?: number;
}
// Return proxy to be used for a particular uri, to be used for request
export function getProxy(_uri: string): ProxyRule | void {
let uri;
try {
uri = new URL(_uri);
} catch (_) {
return;
}
const proxyRules = ConfigUtil.getConfigItem('proxyRules', '').split(';');
// If SPS is on and system uses no proxy then request should not try to use proxy from
// environment. NO_PROXY = '*' makes request ignore all environment proxy variables.
if (proxyRules[0] === '') {
process.env.NO_PROXY = '*';
return;
}
const proxyRule: any = {};
if (uri.protocol === 'http:') {
proxyRules.forEach((proxy: string) => {
if (proxy.includes('http=')) {
proxyRule.hostname = proxy.split('http=')[1].trim().split(':')[0];
proxyRule.port = proxy.split('http=')[1].trim().split(':')[1];
}
});
return proxyRule;
}
if (uri.protocol === 'https:') {
proxyRules.forEach((proxy: string) => {
if (proxy.includes('https=')) {
proxyRule.hostname = proxy.split('https=')[1].trim().split(':')[0];
proxyRule.port = proxy.split('https=')[1].trim().split(':')[1];
}
});
return proxyRule;
}
}
// TODO: Refactor to async function
export async function resolveSystemProxy(mainWindow: Electron.BrowserWindow): Promise<void> {
const page = mainWindow.webContents;

View File

@@ -1,10 +1,10 @@
import {ipcRenderer} from 'electron';
import type WebView from '../components/webview';
import backoff from 'backoff';
import request from 'request';
import type WebView from '../components/webview';
import Logger from './logger-util';
import * as RequestUtil from './request-util';
const logger = new Logger({
file: 'domain-util.log',
@@ -32,23 +32,7 @@ export default class ReconnectUtil {
}
async isOnline(): Promise<boolean> {
return new Promise(resolve => {
try {
request(
{
url: `${this.url}/static/favicon.ico`,
...RequestUtil.requestOptions(this.url)
},
(error: Error, response: request.Response) => {
const isValidResponse =
!error && response.statusCode >= 200 && response.statusCode < 400;
resolve(isValidResponse);
}
);
} catch (error) {
logger.log(error);
}
});
return ipcRenderer.invoke('is-online', this.url);
}
pollInternetAndReload(): void {

View File

@@ -1,67 +0,0 @@
import {remote} from 'electron';
import fs from 'fs';
import path from 'path';
import * as ConfigUtil from './config-util';
import Logger from './logger-util';
import * as ProxyUtil from './proxy-util';
import * as CertificateUtil from './certificate-util';
import * as SystemUtil from './system-util';
const {app} = remote;
const logger = new Logger({
file: 'request-util.log',
timestamp: true
});
interface RequestUtilResponse {
ca: string;
proxy: string | void | ProxyUtil.ProxyRule;
ecdhCurve: 'auto';
headers: { 'User-Agent': string };
}
export function requestOptions(domain: string): RequestUtilResponse {
domain = formatUrl(domain);
const certificate = CertificateUtil.getCertificate(
encodeURIComponent(domain)
);
let certificateFile = null;
if (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(certificateFile, 'utf8');
} catch (error) {
logger.warn('Error while trying to get certificate:', error);
}
}
const proxyEnabled = ConfigUtil.getConfigItem('useManualProxy') || ConfigUtil.getConfigItem('useSystemProxy');
// If certificate for the domain exists add it as a ca key in the request's parameter else consider only domain as the parameter for request
// Add proxy as a parameter if it is being used.
return {
ca: certificateLocation ? certificateLocation : '',
proxy: proxyEnabled ? ProxyUtil.getProxy(domain) : '',
ecdhCurve: 'auto',
headers: {'User-Agent': SystemUtil.getUserAgent()}
};
}
function formatUrl(domain: string): string {
const hasPrefix = domain.startsWith('http', 0);
if (hasPrefix) {
return domain;
}
return domain.includes('localhost:') ? `http://${domain}` : `https://${domain}`;
}

View File

@@ -1,5 +1,4 @@
import {init} from '@sentry/electron';
import isDev from 'electron-is-dev';
export const sentryInit = (): void => {

View File

@@ -1,5 +1,4 @@
import {ipcRenderer} from 'electron';
import os from 'os';
export const connectivityERR: string[] = [

View File

@@ -1,5 +1,7 @@
import path from 'path';
import i18n from 'i18n';
import * as ConfigUtil from './config-util';
i18n.configure({

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 436 B

After

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 900 B

After

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 932 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -81,7 +81,7 @@
"Shortcuts": "Shortcuts",
"Show App Logs": "Show App Logs",
"Show app icon in system tray": "Show app icon in system tray",
"Show app unread badge": "Mostrar una marca en la icona si hi ha missatges sense llegir",
"Show app unread badge": "Mostrar una marca en la icona si hi ha missatges no llegits",
"Show desktop notifications": "Show desktop notifications",
"Show downloaded files in file manager": "Show downloaded files in file manager",
"Show sidebar": "Show sidebar",

View File

@@ -20,57 +20,57 @@
"Change": "Skift",
"Check for Updates": "Tjek for opdateringer",
"Close": "Luk",
"Connect": "Forbind",
"Connect": "Tilslut",
"Connect to another organization": "Forbind til en anden organisation",
"Connected organizations": "Tilsluttede organisationer",
"Copy": "Kopiér",
"Copy Zulip URL": "Kopiér Zulip URL",
"Create a new organization": "Opret ny organisation",
"Cut": "Klip",
"Default download location": "Default download location",
"Default download location": "Default download placering",
"Delete": "Slet",
"Desktop App Settings": "Desktop App Settings",
"Desktop App Settings": "Desktop app-indstillinger",
"Desktop Notifications": "Desktop-notifikationer",
"Desktop Settings": "Desktop-indstillinger",
"Disconnect": "Frakobl",
"Download App Logs": "Download App-logfiler",
"Download App Logs": "Download app-logfiler",
"Edit": "Redigér",
"Edit Shortcuts": "Edit Shortcuts",
"Edit Shortcuts": "Redigér genveje",
"Enable auto updates": "Aktivér auto-opdateringer",
"Enable error reporting (requires restart)": "Enable error reporting (requires restart)",
"Enable spellchecker (requires restart)": "Enable spellchecker (requires restart)",
"Factory Reset": "Factory Reset",
"Enable error reporting (requires restart)": "Aktivér fejlrapportering (kræver genstart)",
"Enable spellchecker (requires restart)": "Aktivér stavekontrol (kræver genstart)",
"Factory Reset": "Nulstil til fabriksindstillinger",
"File": "Fil",
"Find accounts": "Find accounts",
"Find accounts": "Find konti",
"Find accounts by email": "Find accounts by email",
"Flash taskbar on new message": "Flash taskbar on new message",
"Forward": "Forward",
"Functionality": "Functionality",
"General": "General",
"Get beta updates": "Get beta updates",
"Hard Reload": "Hard Reload",
"Help": "Help",
"Help Center": "Help Center",
"History": "History",
"History Shortcuts": "History Shortcuts",
"Keyboard Shortcuts": "Keyboard Shortcuts",
"Log Out": "Log Out",
"Log Out of Organization": "Log Out of Organization",
"Manual proxy configuration": "Manual proxy configuration",
"Minimize": "Minimize",
"Functionality": "Funktionalitet",
"General": "Generelt",
"Get beta updates": " beta opdateringer",
"Hard Reload": "Hård reload",
"Help": "Hlp",
"Help Center": "Hjælpecenter",
"History": "Historik",
"History Shortcuts": "Historik genveje",
"Keyboard Shortcuts": "Tastatur genveje",
"Log Out": "Log ud",
"Log Out of Organization": "Log ud af organisation",
"Manual proxy configuration": "Manuel proxy opsætning",
"Minimize": "Minimér",
"Mute all sounds from Zulip": "Mute all sounds from Zulip",
"NO": "NO",
"Network": "Network",
"OR": "OR",
"Organization URL": "Organization URL",
"Organizations": "Organizations",
"Paste": "Paste",
"Paste and Match Style": "Paste and Match Style",
"NO": "NEJ",
"Network": "Netrk",
"OR": "ELLER",
"Organization URL": "Organisation URL",
"Organizations": "Organisationer",
"Paste": "Indsæt",
"Paste and Match Style": "Indsæt med samme formattering",
"Proxy": "Proxy",
"Proxy bypass rules": "Proxy bypass rules",
"Proxy rules": "Proxy rules",
"Quit": "Quit",
"Quit Zulip": "Quit Zulip",
"Proxy bypass rules": "Proxy bypass regler",
"Proxy rules": "Proxy regler",
"Quit": "Luk",
"Quit Zulip": "Luk Zulip",
"Redo": "Redo",
"Release Notes": "Release Notes",
"Reload": "Reload",

View File

@@ -9,11 +9,11 @@
"All the connected organizations will appear here": "Alle verbundenen Organisationen erscheinen hier",
"Always start minimized": "Immer minimiert öffnen",
"App Updates": "App Updates",
"Appearance": "Ansicht",
"Appearance": "Erscheinungsbild",
"Application Shortcuts": "App-Verknüpfungen",
"Are you sure you want to disconnect this organization?": "Bist du dir sicher, dass du die Verbindung zur Organisation trennen möchtest?",
"Auto hide Menu bar": "Menü automatisch verstecken",
"Auto hide menu bar (Press Alt key to display)": "Menü automatisch verstecken (Zum anzeigen die Alt-Taste drücken)",
"Auto hide menu bar (Press Alt key to display)": "Menü automatisch verstecken (zum Anzeigen die Alt-Taste drücken)",
"Back": "Zurück",
"Bounce dock on new private message": "Im Dock hüpfen, wenn neue private Nachrichten eingehen",
"Certificate file": "Zertifikatsdatei",
@@ -42,7 +42,7 @@
"Factory Reset": "Alle Einstellungen auf Standardwerte zurücksetzen",
"File": "Datei",
"Find accounts": "Accounts finden",
"Find accounts by email": "Accounts anhand E-Mail finden",
"Find accounts by email": "Accounts anhand E-Mail-Adresse finden",
"Flash taskbar on new message": "Farbliche Hervorhebung in Taskbar bei neuen Nachrichten",
"Forward": "Weiter",
"Functionality": "Funktionalität",
@@ -93,7 +93,7 @@
"Tip": "Tipp",
"Toggle DevTools for Active Tab": "Entwickler-Tools für aktiven Tab umschalten",
"Toggle DevTools for Zulip App": "Entwickler-Tools für Zulip-App umschalten",
"Toggle Do Not Disturb": "Nicht-stören-Modus umschalten",
"Toggle Do Not Disturb": "Nicht-Stören-Modus umschalten",
"Toggle Full Screen": "Vollbildschirm umschalten",
"Toggle Sidebar": "Seitenleiste umschalten",
"Toggle Tray Icon": "Tray-Icon umschalten",

View File

@@ -2,7 +2,7 @@
"About Zulip": "About Zulip",
"Actual Size": "Actual Size",
"Add Custom Certificates": "Add Custom Certificates",
"Add Organization": "Add Organization",
"Add Organization": "Add Organisation",
"Add a Zulip organization": "Add a Zulip organisation",
"Add custom CSS": "Add custom CSS",
"Advanced": "Advanced",

View File

@@ -1,16 +1,16 @@
{
"About Zulip": "Tietoa Zulipista",
"Actual Size": "Actual Size",
"Add Custom Certificates": "Add Custom Certificates",
"Add Organization": "Add Organization",
"Add a Zulip organization": "Add a Zulip organization",
"Add custom CSS": "Add custom CSS",
"Advanced": "Advanced",
"Actual Size": "Varsinainen koko",
"Add Custom Certificates": "Lisää omia sertifikaatteja",
"Add Organization": "Lisää organisaatio",
"Add a Zulip organization": "Lisää Zulip-organisaatio",
"Add custom CSS": "Lisää oma CSS",
"Advanced": "Edistynyt",
"All the connected organizations will appear here": "All the connected organizations will appear here",
"Always start minimized": "Always start minimized",
"App Updates": "App Updates",
"Appearance": "Appearance",
"Application Shortcuts": "Application Shortcuts",
"Always start minimized": "Aloita aina pienennettynä",
"App Updates": "Sovellspäivitykset",
"Appearance": "Ulkonäkö",
"Application Shortcuts": "Sovellusoikotiet",
"Are you sure you want to disconnect this organization?": "Are you sure you want to disconnect this organization?",
"Auto hide Menu bar": "Auto hide Menu bar",
"Auto hide menu bar (Press Alt key to display)": "Auto hide menu bar (Press Alt key to display)",
@@ -20,19 +20,19 @@
"Change": "Muuta",
"Check for Updates": "Check for Updates",
"Close": "Sulje",
"Connect": "Connect",
"Connect": "Yhdistä",
"Connect to another organization": "Connect to another organization",
"Connected organizations": "Connected organizations",
"Copy": "Copy",
"Copy Zulip URL": "Copy Zulip URL",
"Create a new organization": "Create a new organization",
"Cut": "Cut",
"Copy": "Kopioi",
"Copy Zulip URL": "Kopioi Zulip-URL",
"Create a new organization": "Luo uusi organisaatio",
"Cut": "Leikkaa",
"Default download location": "Default download location",
"Delete": "Poista",
"Desktop App Settings": "Desktop App Settings",
"Desktop Notifications": "Desktop Notifications",
"Desktop Settings": "Desktop Settings",
"Disconnect": "Disconnect",
"Disconnect": "Katkaise",
"Download App Logs": "Download App Logs",
"Edit": "Muokkaa",
"Edit Shortcuts": "Edit Shortcuts",
@@ -49,8 +49,8 @@
"General": "General",
"Get beta updates": "Get beta updates",
"Hard Reload": "Hard Reload",
"Help": "Help",
"Help Center": "Help Center",
"Help": "Ohje",
"Help Center": "Tukikeskus",
"History": "History",
"History Shortcuts": "History Shortcuts",
"Keyboard Shortcuts": "Keyboard Shortcuts",
@@ -59,26 +59,26 @@
"Manual proxy configuration": "Manual proxy configuration",
"Minimize": "Minimize",
"Mute all sounds from Zulip": "Mute all sounds from Zulip",
"NO": "NO",
"NO": "EI",
"Network": "Network",
"OR": "TAI",
"Organization URL": "Organisaation URL",
"Organizations": "Organizations",
"Organizations": "Organisaatiot",
"Paste": "Liitä",
"Paste and Match Style": "Paste and Match Style",
"Proxy": "Proxy",
"Paste and Match Style": "Liitä ja täsmää tyylit",
"Proxy": "Välipalvelin",
"Proxy bypass rules": "Proxy bypass rules",
"Proxy rules": "Proxy rules",
"Quit": "Lopeta",
"Quit Zulip": "Quit Zulip",
"Redo": "Redo",
"Quit Zulip": "Lopeta Zulip",
"Redo": "Toista",
"Release Notes": "Release Notes",
"Reload": "Reload",
"Reload": "Lataa uudelleen",
"Report an Issue": "Report an Issue",
"Save": "Talleta",
"Save": "Tallenna",
"Select All": "Valitse kaikki",
"Settings": "Asetukset",
"Shortcuts": "Shortcuts",
"Shortcuts": "Oikopolut",
"Show App Logs": "Show App Logs",
"Show app icon in system tray": "Show app icon in system tray",
"Show app unread badge": "Show app unread badge",
@@ -90,22 +90,22 @@
"Switch to Previous Organization": "Switch to Previous Organization",
"These desktop app shortcuts extend the Zulip webapp's": "These desktop app shortcuts extend the Zulip webapp's",
"This will delete all application data including all added accounts and preferences": "This will delete all application data including all added accounts and preferences",
"Tip": "Tip",
"Tip": "Vinkki",
"Toggle DevTools for Active Tab": "Toggle DevTools for Active Tab",
"Toggle DevTools for Zulip App": "Toggle DevTools for Zulip App",
"Toggle Do Not Disturb": "Toggle Do Not Disturb",
"Toggle Full Screen": "Toggle Full Screen",
"Toggle Sidebar": "Toggle Sidebar",
"Toggle Tray Icon": "Toggle Tray Icon",
"Tools": "Tools",
"Undo": "Undo",
"Tools": "Työkalut",
"Undo": "Peru",
"Upload": "Upload",
"Use system proxy settings (requires restart)": "Use system proxy settings (requires restart)",
"View": "View",
"View": "Näytä",
"View Shortcuts": "View Shortcuts",
"Window": "Window",
"Window": "Ikkuna",
"Window Shortcuts": "Window Shortcuts",
"YES": "YES",
"YES": "KYLLÄ",
"Zoom In": "Zoom In",
"Zoom Out": "Zoom Out",
"Zulip Help": "Zulip Help",

View File

@@ -112,23 +112,23 @@
"keyboard shortcuts": "gyorsbillentyűk",
"script": "parancsfájl",
"Quit when the window is closed": "Kilépés az ablak bezárásakor",
"Ask where to save files before downloading": "Ask where to save files before downloading",
"Services": "Services",
"Hide": "Hide",
"Hide Others": "Hide Others",
"Unhide": "Unhide",
"AddServer": "AddServer",
"App language (requires restart)": "App language (requires restart)",
"Factory Reset Data": "Factory Reset Data",
"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Reset the application, thus deleting all the connected organizations, accounts, and certificates.",
"On macOS, the OS spellchecker is used.": "On macOS, the OS spellchecker is used.",
"Change the language from System Preferences → Keyboard → Text → Spelling.": "Change the language from System Preferences → Keyboard → Text → Spelling.",
"Copy Link": "Copy Link",
"Copy Image": "Copy Image",
"Copy Image URL": "Copy Image URL",
"No Suggestion Found": "No Suggestion Found",
"You can select a maximum of 3 languages for spellchecking.": "You can select a maximum of 3 languages for spellchecking.",
"Spellchecker Languages": "Spellchecker Languages",
"Add to Dictionary": "Add to Dictionary",
"Look Up": "Look Up"
"Ask where to save files before downloading": "Letöltés előtt kérdezze meg a mentés helyét",
"Services": "Szolgáltatások",
"Hide": "Elrejtés",
"Hide Others": "A többi elrejtése",
"Unhide": "Felfedés",
"AddServer": "Szerver hozzáadás",
"App language (requires restart)": "Alkalmazás nyelve (újraindítást igényel)",
"Factory Reset Data": "Gyári adatok visszaállítása",
"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Alkalmazásadatok törlése - törlődnek a csatlakoztatott szervezetek, fiókok és tanúsítványok is.",
"On macOS, the OS spellchecker is used.": "MacOS rendszereken az operációs rendszer helyesírásellenőrzőjét használjuk.",
"Change the language from System Preferences → Keyboard → Text → Spelling.": "A nyelv a Rendszerbeállítások → Billentyűzet → Szöveg → Helyesírás pontban változtatható meg.",
"Copy Link": "Hivatkozás másolása",
"Copy Image": "Kép másolása",
"Copy Image URL": "Kép címének másolása",
"No Suggestion Found": "Nem találtunk javaslatot",
"You can select a maximum of 3 languages for spellchecking.": "Maximum 3 nyelv választható ki helyesírásellenőrzéshez.",
"Spellchecker Languages": "Helyesírásellenőrzés nyelvei",
"Add to Dictionary": "Hozzáadás a szótárhoz",
"Look Up": "Keresés"
}

View File

@@ -105,23 +105,23 @@
"View Shortcuts": "Visualizza Scorciatoie",
"Window": "Finestra",
"Window Shortcuts": "Scorciatoie Finestra",
"YES": "YES",
"Zoom In": "Zoom Più",
"Zoom Out": "Zoom Meno",
"Zulip Help": "Aiuto Zulip",
"YES": "SI",
"Zoom In": "Ingrandisci",
"Zoom Out": "Riduci",
"Zulip Help": "Guida di Zulip",
"keyboard shortcuts": "scorciatoie tastiera",
"script": "script",
"Quit when the window is closed": "Esci quando la finestra è chiusa",
"Ask where to save files before downloading": "Chiedi dove salvare i file prima di scaricarli",
"Services": "Servizi",
"Hide": "Nascondi",
"Hide Others": "Hide Others",
"Unhide": "Unhide",
"Hide Others": "Nascondi gli altri",
"Unhide": "Mostra",
"AddServer": "AddServer",
"App language (requires restart)": "App language (requires restart)",
"App language (requires restart)": "Lingua applicazione (richiede riavvio)",
"Factory Reset Data": "Factory Reset Data",
"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Reset the application, thus deleting all the connected organizations, accounts, and certificates.",
"On macOS, the OS spellchecker is used.": "On macOS, the OS spellchecker is used.",
"On macOS, the OS spellchecker is used.": "Su macOS, è usato il correttore ortografico di sistema.",
"Change the language from System Preferences → Keyboard → Text → Spelling.": "Change the language from System Preferences → Keyboard → Text → Spelling.",
"Copy Link": "Copia link",
"Copy Image": "Copia immagine",

View File

@@ -24,13 +24,13 @@
"Connect to another organization": "Подключиться к другой организации",
"Connected organizations": "Подключенные организации",
"Copy": "Копировать",
"Copy Zulip URL": "Скопировать URL Zulip",
"Copy Zulip URL": "Скопировать ссылку на сервер Zulip",
"Create a new organization": "Создать новую организацию",
"Cut": "Вырезать",
"Default download location": "Папка для загрузки",
"Delete": "Удалить",
"Desktop App Settings": "Настройки приложения",
"Desktop Notifications": "Всплывающие оповещения",
"Desktop Notifications": "Оповещения на рабочем столе",
"Desktop Settings": "Настройки рабочего стола",
"Disconnect": "Отключиться",
"Download App Logs": "Скачать логи приложения",
@@ -111,7 +111,7 @@
"Zulip Help": "Помощь по Zulip",
"keyboard shortcuts": "Горячие клавиши",
"script": "скрипт",
"Quit when the window is closed": "Выходить при закрытии окна",
"Quit when the window is closed": "Выйти, когда окно будет закрыто",
"Ask where to save files before downloading": "Спрашивать, где сохранять файлы перед скачиванием",
"Services": "Сервисы",
"Hide": "Скрыть",

View File

@@ -5,8 +5,8 @@
"cs": "česky",
"da": "Dansk",
"de": "Deutsch",
"el_GR": "Greek (Greece)",
"el": "Ελληνικά",
"el-GR": "Greek (Greece)",
"en_GB": "English (UK)",
"en": "English (US)",
"es": "Español",
@@ -29,6 +29,7 @@
"pt": "Português",
"ro": "Română",
"ru": "Русский",
"sk": "Slovak",
"sr": "српски",
"sv": "svenska",
"ta": "தமிழ்",
@@ -37,6 +38,6 @@
"uz": "O'zbek",
"vi": "Tiếng Việt",
"zh_TW": "中文 (傳統的)",
"zh-hans": "简体中文",
"zh-hant": "繁體中文"
"zh-Hans": "简体中文",
"zh-Hant": "繁體中文"
}

View File

@@ -1,134 +1,134 @@
{
"About Zulip": "About Zulip",
"Actual Size": "Actual Size",
"Add Custom Certificates": "Add Custom Certificates",
"Add Organization": "Add Organization",
"Add a Zulip organization": "Add a Zulip organization",
"Add custom CSS": "Add custom CSS",
"Advanced": "Advanced",
"All the connected organizations will appear here": "All the connected organizations will appear here",
"Always start minimized": "Always start minimized",
"App Updates": "App Updates",
"Appearance": "Appearance",
"Application Shortcuts": "Application Shortcuts",
"Are you sure you want to disconnect this organization?": "Are you sure you want to disconnect this organization?",
"Auto hide Menu bar": "Auto hide Menu bar",
"Auto hide menu bar (Press Alt key to display)": "Auto hide menu bar (Press Alt key to display)",
"Back": "Back",
"Bounce dock on new private message": "Bounce dock on new private message",
"Certificate file": "Certificate file",
"Change": "Change",
"Check for Updates": "Check for Updates",
"Close": "Close",
"Connect": "Connect",
"Connect to another organization": "Connect to another organization",
"Connected organizations": "Connected organizations",
"Copy": "Copy",
"Copy Zulip URL": "Copy Zulip URL",
"Create a new organization": "Create a new organization",
"Cut": "Cut",
"Default download location": "Default download location",
"Delete": "Delete",
"Desktop App Settings": "Desktop App Settings",
"Desktop Notifications": "Desktop Notifications",
"Desktop Settings": "Desktop Settings",
"Disconnect": "Disconnect",
"Download App Logs": "Download App Logs",
"Edit": "Edit",
"Edit Shortcuts": "Edit Shortcuts",
"Enable auto updates": "Enable auto updates",
"Enable error reporting (requires restart)": "Enable error reporting (requires restart)",
"Enable spellchecker (requires restart)": "Enable spellchecker (requires restart)",
"Factory Reset": "Factory Reset",
"File": "File",
"Find accounts": "Find accounts",
"Find accounts by email": "Find accounts by email",
"Flash taskbar on new message": "Flash taskbar on new message",
"Forward": "Forward",
"Functionality": "Functionality",
"General": "General",
"Get beta updates": "Get beta updates",
"Hard Reload": "Hard Reload",
"Help": "Help",
"Help Center": "Help Center",
"History": "History",
"History Shortcuts": "History Shortcuts",
"Keyboard Shortcuts": "Keyboard Shortcuts",
"Log Out": "Log Out",
"Log Out of Organization": "Log Out of Organization",
"Manual proxy configuration": "Manual proxy configuration",
"Minimize": "Minimize",
"Mute all sounds from Zulip": "Mute all sounds from Zulip",
"NO": "NO",
"Network": "Network",
"About Zulip": "關於 Zulip",
"Actual Size": "實際大小",
"Add Custom Certificates": "新增自定義證書",
"Add Organization": "新增組織",
"Add a Zulip organization": "新增 Zulip 組織",
"Add custom CSS": "新增自定義 CSS",
"Advanced": "\b\b進階",
"All the connected organizations will appear here": "所有已連結的組織將會顯示於此",
"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": "Bounce dock on 新的私人訊息",
"Certificate file": "證書檔案",
"Change": "修改",
"Check for Updates": "檢查更新",
"Close": "關閉",
"Connect": "連結",
"Connect to another organization": "連結至其他組織",
"Connected organizations": "已連結的組織",
"Copy": "複製",
"Copy Zulip URL": "複製 Zulip 網址",
"Create a new organization": "新增組織",
"Cut": "剪下",
"Default download location": "預設下載位置",
"Delete": "刪除",
"Desktop App Settings": "桌面版應用程式設定",
"Desktop Notifications": "桌面版通知",
"Desktop Settings": "桌面版設定",
"Disconnect": "斷開連結",
"Download App Logs": "下載應用程式紀錄",
"Edit": "編輯",
"Edit Shortcuts": "編輯快捷鍵",
"Enable auto updates": "啟用自動更新",
"Enable error reporting (requires restart)": "啟用錯誤回報(需要重新啟動)",
"Enable spellchecker (requires restart)": "啟用拼字檢查(需要重新啟動)",
"Factory Reset": "初始化",
"File": "檔案",
"Find accounts": "查詢帳號",
"Find accounts by email": " email 查詢帳號",
"Flash taskbar on new message": "有新訊息時閃爍任務欄",
"Forward": "轉發",
"Functionality": "功能性",
"General": "通用",
"Get beta updates": "取得 beta 更新",
"Hard Reload": "強制重新載入",
"Help": "幫助",
"Help Center": "幫助中心",
"History": "歷史",
"History Shortcuts": "歷史快捷鍵",
"Keyboard Shortcuts": "Keyboard 快捷鍵",
"Log Out": "登出",
"Log Out of Organization": "登出此組織",
"Manual proxy configuration": "proxy 設定",
"Minimize": "最小化",
"Mute all sounds from Zulip": "將所有 Zulip 音效靜音",
"NO": "",
"Network": "網路",
"OR": "或",
"Organization URL": "Organization URL",
"Organizations": "Organizations",
"Paste": "Paste",
"Paste and Match Style": "Paste and Match Style",
"Organization URL": "組織網址",
"Organizations": "組織",
"Paste": "貼上",
"Paste and Match Style": "貼上並套用樣式",
"Proxy": "Proxy",
"Proxy bypass rules": "Proxy bypass rules",
"Proxy rules": "Proxy rules",
"Quit": "Quit",
"Quit Zulip": "Quit Zulip",
"Redo": "Redo",
"Release Notes": "Release Notes",
"Reload": "Reload",
"Report an Issue": "Report an Issue",
"Save": "Save",
"Select All": "Select All",
"Settings": "Settings",
"Shortcuts": "Shortcuts",
"Show App Logs": "Show App Logs",
"Show app icon in system tray": "Show app icon in system tray",
"Show app unread badge": "Show app unread badge",
"Show desktop notifications": "Show desktop notifications",
"Show downloaded files in file manager": "Show downloaded files in file manager",
"Show sidebar": "Show sidebar",
"Start app at login": "Start app at login",
"Switch to Next Organization": "Switch to Next Organization",
"Switch to Previous Organization": "Switch to Previous Organization",
"These desktop app shortcuts extend the Zulip webapp's": "These desktop app shortcuts extend the Zulip webapp's",
"This will delete all application data including all added accounts and preferences": "This will delete all application data including all added accounts and preferences",
"Tip": "Tip",
"Toggle DevTools for Active Tab": "Toggle DevTools for Active Tab",
"Toggle DevTools for Zulip App": "Toggle DevTools for Zulip App",
"Toggle Do Not Disturb": "Toggle Do Not Disturb",
"Toggle Full Screen": "Toggle Full Screen",
"Toggle Sidebar": "Toggle Sidebar",
"Toggle Tray Icon": "Toggle Tray Icon",
"Tools": "Tools",
"Undo": "Undo",
"Upload": "Upload",
"Use system proxy settings (requires restart)": "Use system proxy settings (requires restart)",
"View": "View",
"View Shortcuts": "View Shortcuts",
"Window": "Window",
"Window Shortcuts": "Window Shortcuts",
"YES": "YES",
"Zoom In": "Zoom In",
"Zoom Out": "Zoom Out",
"Zulip Help": "Zulip Help",
"keyboard shortcuts": "keyboard shortcuts",
"Proxy bypass rules": "Proxy 白名單規則",
"Proxy rules": "Proxy 規則",
"Quit": "退出",
"Quit Zulip": "退出 Zulip",
"Redo": "重做",
"Release Notes": "發行公告",
"Reload": "重新載入",
"Report an Issue": "回報問題",
"Save": "儲存",
"Select All": "選擇全部",
"Settings": "設定",
"Shortcuts": "快捷鍵",
"Show App Logs": "顯示應用程式記錄",
"Show app icon in system tray": "顯示應用程式圖示在系統夾",
"Show app unread badge": "顯示應用程式未讀標記",
"Show desktop notifications": "顯示桌面版通知",
"Show downloaded files in file manager": "顯示下載檔案於檔案管理中",
"Show sidebar": "顯示側邊欄",
"Start app at login": "登入時開啟應用程式",
"Switch to Next Organization": "切換至後一個組織",
"Switch to Previous Organization": "切換至前一個組織",
"These desktop app shortcuts extend the Zulip webapp's": "這些桌面版快捷鍵是從 Zulip web 版應用程式擴展而來",
"This will delete all application data including all added accounts and preferences": "這樣將會刪除所有應用程式的資料,包含所有帳號跟其設定",
"Tip": "提示",
"Toggle DevTools for Active Tab": "切換 DevTools for Active Tab",
"Toggle DevTools for Zulip App": "切換 DevTools for Zulip App",
"Toggle Do Not Disturb": "切換勿擾模式",
"Toggle Full Screen": "切換全螢幕",
"Toggle Sidebar": "切換側邊欄",
"Toggle Tray Icon": "切換夾圖示",
"Tools": "工具",
"Undo": "復原",
"Upload": "上傳",
"Use system proxy settings (requires restart)": "使用系統 proxy 設定(需要重新啟動)",
"View": "檢視",
"View Shortcuts": "檢視快捷鍵",
"Window": "視窗",
"Window Shortcuts": "視窗快捷鍵",
"YES": "",
"Zoom In": "放大",
"Zoom Out": "縮小",
"Zulip Help": "Zulip 幫助",
"keyboard shortcuts": "鍵盤快捷鍵",
"script": "script",
"Quit when the window is closed": "Quit when the window is closed",
"Ask where to save files before downloading": "Ask where to save files before downloading",
"Services": "Services",
"Hide": "Hide",
"Hide Others": "Hide Others",
"Unhide": "Unhide",
"AddServer": "AddServer",
"App language (requires restart)": "App language (requires restart)",
"Factory Reset Data": "Factory Reset Data",
"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "Reset the application, thus deleting all the connected organizations, accounts, and certificates.",
"On macOS, the OS spellchecker is used.": "On macOS, the OS spellchecker is used.",
"Change the language from System Preferences → Keyboard → Text → Spelling.": "Change the language from System Preferences → Keyboard → Text → Spelling.",
"Copy Link": "Copy Link",
"Copy Image": "Copy Image",
"Copy Image URL": "Copy Image URL",
"No Suggestion Found": "No Suggestion Found",
"You can select a maximum of 3 languages for spellchecking.": "You can select a maximum of 3 languages for spellchecking.",
"Spellchecker Languages": "Spellchecker Languages",
"Add to Dictionary": "Add to Dictionary",
"Look Up": "Look Up"
"Quit when the window is closed": "當關閉視窗時退出",
"Ask where to save files before downloading": "下載前詢問檔案儲存位置",
"Services": "服務",
"Hide": "隱藏",
"Hide Others": "隱藏其他",
"Unhide": "取消隱藏",
"AddServer": "新增伺服器",
"App language (requires restart)": "應用程式語言(需要重新啟動)",
"Factory Reset Data": "設定初始化",
"Reset the application, thus deleting all the connected organizations, accounts, and certificates.": "重置應用程式,此操作將刪除:已連結的組織、帳號、跟證書",
"On macOS, the OS spellchecker is used.": " macOS 下使用作業系統的拼字檢查",
"Change the language from System Preferences → Keyboard → Text → Spelling.": "變更語言:System Preferences → Keyboard → Text → Spelling.",
"Copy Link": "複製網址",
"Copy Image": "複製圖",
"Copy Image URL": "複製圖網址",
"No Suggestion Found": "找不到建議事項",
"You can select a maximum of 3 languages for spellchecking.": "您最多可以選擇 3 個語言拼字檢查",
"Spellchecker Languages": "需要拼字檢查的語言",
"Add to Dictionary": "新增至資料夾",
"Look Up": "查詢"
}

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

After

Width:  |  Height:  |  Size: 106 KiB

BIN
build/icons/1024x1024.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 737 B

After

Width:  |  Height:  |  Size: 581 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 797 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -2,6 +2,57 @@
All notable changes to the Zulip desktop app are documented in this file.
### v5.4.1-beta --2020-07-29
**Fixes**:
* Resized the large application icon on macOS dock to be coherent with other icons.
**Potential Fixes**:
* macOS: Electron 9 upgrade is a potential fix for the ['grey screen issue'](https://chat.zulip.org/#narrow/stream/9-issues/topic/Grey.20Window.20on.20macOS) reported.
**Dependencies**:
* Upgrade all dependencies, including Electron 9.1.1.
### v5.4.0 --2020-07-21
**New features**:
* Added support for certificates from system store.
* Added support for Slovak as application language.
**Fixes**:
* Fix bug in *Copy Link* and add *Copy Email* option in context menu.
* Enable *Copy* option in context menu only when copying is possible.
* Remove leading and trailing separators in context menu on non-mac systems.
* ignoreCerts: Accommodate WebSocket URLs in certificate-error handler.
**Dependencies**:
* Upgrade all dependencies, including Electron 8.4.0.
**Deprecations**:
* This release supports certificates from Zulip store as well as system store. Zulip certificate store will be deprecated in the next release.
Users are hereby requested to move to system store. For more information, please see the [documentation](https://zulip.com/help/custom-certificates).
### v5.3.0 --2020-06-24
**Security fixes**:
* Remove the insecure ignoreCerts option.
**Fixes**:
* Windows: Turn off start at login by default.
* Fix zoom issues where some webviews would not get zoomed-out once zoomed-in.
* Fix overflowing contents on 'Add Organization' view.
**New features**:
* Add a cancel button in the report-issue modal.
* macOS: Use electron API to get dark tray icon instead of the green icon for the light theme.
* Remove 'Reset App Data' option. Factory Reset option has been moved to Settings → General.
* Support pkg installer on macOS.
* Use electron 8 built-in spellchecker. Linux and Windows users can now choose upto three spellchecker languages from Settings → General. On macOS, default spellchecker is used.
* Setup Transifex for better synchronization of translations. The application now supports 41 languages instead of 21.
**Dependencies**:
* Upgrade all dependencies, including Electron 8.3.3.
### v5.2.0 --2020-05-04
**Security fixes**:

View File

@@ -1,16 +1,16 @@
'use strict';
const gulp = require('gulp');
const {execSync} = require('child_process');
const electron = require('electron-connect').server.create({
verbose: true
});
const tape = require('gulp-tape');
const tapColorize = require('tap-colorize');
const ts = require('gulp-typescript');
const tsProject = ts.createProject('tsconfig.json');
const glob = require('glob');
const {execSync} = require('child_process');
const gulp = require('gulp');
const tape = require('gulp-tape');
const ts = require('gulp-typescript');
const tapColorize = require('tap-colorize');
const tsProject = ts.createProject('tsconfig.json');
const baseFilePattern = 'app/+(main|renderer)/**/*';
const globOptions = {cwd: __dirname};
const jsFiles = glob.sync(baseFilePattern + '.js', globOptions);

5557
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "zulip",
"productName": "Zulip",
"version": "5.3.0",
"version": "5.4.2",
"main": "./app/main",
"description": "Zulip Desktop App",
"license": "Apache-2.0",
@@ -50,7 +50,7 @@
"files": [
"app/**/*"
],
"copyright": "©2019 Kandra Labs, Inc.",
"copyright": "©2020 Kandra Labs, Inc.",
"mac": {
"category": "public.app-category.productivity",
"target": [
@@ -146,22 +146,22 @@
],
"dependencies": {
"@electron-elements/send-feedback": "^2.0.3",
"@sentry/electron": "^1.3.0",
"@yaireo/tagify": "^3.10.2",
"adm-zip": "^0.4.14",
"@sentry/electron": "^1.5.0",
"@yaireo/tagify": "^3.15.4",
"adm-zip": "^0.4.16",
"auto-launch": "^5.0.5",
"backoff": "^2.5.0",
"electron-is-dev": "^1.2.0",
"electron-log": "^4.1.2",
"electron-log": "^4.2.2",
"electron-updater": "^4.3.1",
"electron-window-state": "^5.0.3",
"escape-html": "^1.0.3",
"fs-extra": "^9.0.0",
"i18n": "^0.9.1",
"fs-extra": "^9.0.1",
"get-stream": "^5.1.0",
"i18n": "^0.10.0",
"iso-639-1": "^2.1.3",
"nan": "^2.14.0",
"node-json-db": "^1.1.0",
"request": "^2.88.2",
"semver": "^7.3.2"
},
"optionalDependencies": {
@@ -171,41 +171,50 @@
"@types/adm-zip": "^0.4.33",
"@types/auto-launch": "^5.0.1",
"@types/backoff": "^2.5.1",
"@types/escape-html": "0.0.20",
"@types/fs-extra": "^8.1.0",
"@types/escape-html": "^1.0.0",
"@types/fs-extra": "^9.0.1",
"@types/i18n": "^0.8.6",
"@types/node": "^13.13.4",
"@types/request": "^2.48.4",
"@types/node": "^14.0.25",
"@types/requestidlecallback": "^0.3.1",
"@typescript-eslint/eslint-plugin": "^2.30.0",
"@typescript-eslint/parser": "^2.30.0",
"@typescript-eslint/eslint-plugin": "^3.7.0",
"@typescript-eslint/parser": "^3.7.0",
"devtron": "^1.4.0",
"dotenv": "^8.2.0",
"electron": "^8.3.3",
"electron-builder": "^22.6.0",
"electron": "^9.2.0",
"electron-builder": "^22.8.0",
"electron-connect": "^0.6.3",
"electron-notarize": "^0.3.0",
"eslint-config-xo-typescript": "^0.28.0",
"electron-notarize": "^1.0.0",
"glob": "^7.1.6",
"gulp": "^4.0.2",
"gulp-tape": "^1.0.0",
"gulp-typescript": "^6.0.0-alpha.1",
"htmlhint": "^0.11.0",
"nodemon": "^2.0.3",
"htmlhint": "^0.14.1",
"nodemon": "^2.0.4",
"pre-commit": "^1.2.2",
"rimraf": "^3.0.2",
"spectron": "^10.0.1",
"stylelint": "^13.3.3",
"spectron": "^11.1.0",
"stylelint": "^13.6.1",
"tap-colorize": "^1.2.0",
"tape": "^5.0.0",
"typescript": "^3.8.3",
"xo": "^0.30.0"
"tape": "^5.0.1",
"typescript": "^3.9.7",
"xo": "^0.32.1"
},
"xo": {
"rules": {
"@typescript-eslint/no-dynamic-delete": "off",
"@typescript-eslint/prefer-readonly-parameter-types": "off",
"arrow-body-style": "error",
"import/first": "error",
"import/newline-after-import": "error",
"import/order": [
"error",
{
"alphabetize": {
"order": "asc"
},
"newlines-between": "always"
}
],
"import/unambiguous": "error",
"max-lines": [
"warn",

View File

@@ -1,11 +1,11 @@
'use strict';
const path = require('path');
const dotenv = require('dotenv');
const {notarize} = require('electron-notarize');
dotenv.config({path: path.join(__dirname, '/../.env')});
const {notarize} = require('electron-notarize');
exports.default = async function (context) {
const {electronPlatformName, appOutDir} = context;
if (electronPlatformName !== 'darwin') {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -1,5 +1,6 @@
'use strict';
const test = require('tape');
const setup = require('./setup');
test('app runs', async t => {

View File

@@ -1,8 +1,9 @@
'use strict';
const {Application} = require('spectron');
const fs = require('fs');
const path = require('path');
const rimraf = require('rimraf');
const {Application} = require('spectron');
const config = require('./config');

View File

@@ -1,5 +1,6 @@
'use strict';
const test = require('tape');
const setup = require('./setup');
test('add-organization', async t => {

View File

@@ -1,5 +1,6 @@
'use strict';
const test = require('tape');
const setup = require('./setup');
// Create new org link should open in the default browser [WIP]

9
typings.d.ts vendored
View File

@@ -1,3 +1,8 @@
declare module 'electron' {
// https://github.com/electron/typescript-definitions/issues/170
interface IncomingMessage extends NodeJS.ReadableStream {}
}
declare module '@electron-elements/send-feedback' {
class SendFeedback extends HTMLElement {
customStyles: string;
@@ -9,11 +14,13 @@ declare module '@electron-elements/send-feedback' {
buttonLabel: string;
loaderSuccessText: string;
logs: string[];
useReporter: (reporter: string, data: object) => void;
useReporter: (reporter: string, data: Record<string, unknown>) => void;
}
export = SendFeedback;
}
declare module 'electron-connect';
declare module 'node-mac-notifier';
declare module '@yaireo/tagify';