Compare commits

...

12 Commits

Author SHA1 Message Date
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
52 changed files with 1513 additions and 2200 deletions

View File

@@ -6,7 +6,8 @@
Desktop client for Zulip. Available for Mac, Linux, and Windows. 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 # Download
Please see the [installation guide](https://zulip.com/help/desktop-app-install-guide). Please see the [installation guide](https://zulip.com/help/desktop-app-install-guide).

View File

@@ -1,13 +1,15 @@
import {app, dialog, session} from 'electron'; import {app, dialog, session} from 'electron';
import {UpdateDownloadedEvent, UpdateInfo, autoUpdater} from 'electron-updater';
import util from 'util'; import util from 'util';
import {linuxUpdateNotification} from './linuxupdater'; // Required only in case of linux
import log from 'electron-log';
import isDev from 'electron-is-dev'; 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 ConfigUtil from '../renderer/js/utils/config-util';
import * as LinkUtil from '../renderer/js/utils/link-util'; import * as LinkUtil from '../renderer/js/utils/link-util';
import {linuxUpdateNotification} from './linuxupdater'; // Required only in case of linux
const sleep = util.promisify(setTimeout); const sleep = util.promisify(setTimeout);
export async function appUpdater(updateFromMenu = false): Promise<void> { export async function appUpdater(updateFromMenu = false): Promise<void> {

View File

@@ -1,18 +1,20 @@
import {sentryInit} from '../renderer/js/utils/sentry-util';
import {appUpdater} from './autoupdater'; import electron, {app, dialog, ipcMain, session} from 'electron';
import {setAutoLaunch} from './startup'; import fs from 'fs';
import path from 'path';
import windowStateKeeper from 'electron-window-state'; 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 BadgeSettings from '../renderer/js/pages/preference/badge-settings';
import * as CertificateUtil from '../renderer/js/utils/certificate-util'; import * as CertificateUtil from '../renderer/js/utils/certificate-util';
import * as ConfigUtil from '../renderer/js/utils/config-util'; import * as ConfigUtil from '../renderer/js/utils/config-util';
import * as ProxyUtil from '../renderer/js/utils/proxy-util'; import * as ProxyUtil from '../renderer/js/utils/proxy-util';
import {sentryInit} from '../renderer/js/utils/sentry-util';
import {appUpdater} from './autoupdater';
import * as AppMenu from './menu';
import {_getServerSettings, _saveServerIcon, _isOnline} from './request'; import {_getServerSettings, _saveServerIcon, _isOnline} from './request';
import {setAutoLaunch} from './startup';
let mainWindowState: windowStateKeeper.State; let mainWindowState: windowStateKeeper.State;
@@ -353,7 +355,7 @@ ${error}`
AppMenu.setMenu(props); AppMenu.setMenu(props);
const activeTab = props.tabs[props.activeTabIndex]; const activeTab = props.tabs[props.activeTabIndex];
if (activeTab) { if (activeTab) {
mainWindow.setTitle(`Zulip - ${activeTab.webview.props.name}`); mainWindow.setTitle(`Zulip - ${activeTab.webviewName}`);
} }
}); });

View File

@@ -2,9 +2,11 @@ import {app, Notification, net} from 'electron';
import getStream from 'get-stream'; import getStream from 'get-stream';
import semver from 'semver'; import semver from 'semver';
import * as ConfigUtil from '../renderer/js/utils/config-util'; import * as ConfigUtil from '../renderer/js/utils/config-util';
import * as LinuxUpdateUtil from '../renderer/js/utils/linux-update-util'; import * as LinuxUpdateUtil from '../renderer/js/utils/linux-update-util';
import Logger from '../renderer/js/utils/logger-util'; import Logger from '../renderer/js/utils/logger-util';
import {fetchResponse} from './request'; import {fetchResponse} from './request';
const logger = new Logger({ const logger = new Logger({

View File

@@ -1,15 +1,17 @@
import {app, shell, BrowserWindow, Menu} from 'electron'; import {app, shell, BrowserWindow, Menu} from 'electron';
import {appUpdater} from './autoupdater';
import AdmZip from 'adm-zip'; 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 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 LinkUtil from '../renderer/js/utils/link-util';
import * as t from '../renderer/js/utils/translation-util'; import * as t from '../renderer/js/utils/translation-util';
import type {ServerOrFunctionalTab} from '../renderer/js/main';
import {appUpdater} from './autoupdater';
export interface MenuProps { export interface MenuProps {
tabs: ServerOrFunctionalTab[]; tabs: TabData[];
activeTabIndex?: number; activeTabIndex?: number;
enableMenu?: boolean; enableMenu?: boolean;
} }
@@ -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[] = [{ const initialSubmenu: Electron.MenuItemConstructorOptions[] = [{
label: t.__('Minimize'), label: t.__('Minimize'),
role: 'minimize' role: 'minimize'
@@ -229,17 +231,17 @@ function getWindowSubmenu(tabs: ServerOrFunctionalTab[], activeTabIndex: number)
}); });
tabs.forEach(tab => { tabs.forEach(tab => {
// Do not add functional tab settings to list of windows in menu bar // 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; return;
} }
initialSubmenu.push({ initialSubmenu.push({
label: tab.props.name, label: tab.name,
accelerator: tab.props.role === 'function' ? '' : `${ShortcutKey} + ${tab.props.index + 1}`, accelerator: tab.role === 'function' ? '' : `${ShortcutKey} + ${tab.index + 1}`,
checked: tab.props.index === activeTabIndex, checked: tab.index === activeTabIndex,
click(_item, focusedWindow) { click(_item, focusedWindow) {
if (focusedWindow) { if (focusedWindow) {
sendAction('switch-server-tab', tab.props.index); sendAction('switch-server-tab', tab.index);
} }
}, },
type: 'checkbox' type: 'checkbox'
@@ -545,20 +547,20 @@ async function checkForUpdate(): Promise<void> {
await appUpdater(true); await appUpdater(true);
} }
function getNextServer(tabs: ServerOrFunctionalTab[], activeTabIndex: number): number { function getNextServer(tabs: TabData[], activeTabIndex: number): number {
do { do {
activeTabIndex = (activeTabIndex + 1) % tabs.length; activeTabIndex = (activeTabIndex + 1) % tabs.length;
} }
while (tabs[activeTabIndex].props.role !== 'server'); while (tabs[activeTabIndex].role !== 'server');
return activeTabIndex; return activeTabIndex;
} }
function getPreviousServer(tabs: ServerOrFunctionalTab[], activeTabIndex: number): number { function getPreviousServer(tabs: TabData[], activeTabIndex: number): number {
do { do {
activeTabIndex = (activeTabIndex - 1 + tabs.length) % tabs.length; activeTabIndex = (activeTabIndex - 1 + tabs.length) % tabs.length;
} }
while (tabs[activeTabIndex].props.role !== 'server'); while (tabs[activeTabIndex].role !== 'server');
return activeTabIndex; return activeTabIndex;
} }

View File

@@ -3,12 +3,14 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import stream from 'stream'; import stream from 'stream';
import util from 'util'; import util from 'util';
import * as Messages from '../resources/messages';
import Logger from '../renderer/js/utils/logger-util';
import {ServerConf} from '../renderer/js/utils/domain-util';
import escape from 'escape-html'; import escape from 'escape-html';
import getStream from 'get-stream'; 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> { export async function fetchResponse(request: ClientRequest): Promise<IncomingMessage> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
request.on('response', resolve); request.on('response', resolve);

View File

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

View File

@@ -1,5 +1,5 @@
import {clipboard} from 'electron';
import crypto from 'crypto'; import crypto from 'crypto';
import {clipboard} from 'electron';
// This helper is exposed via electron_bridge for use in the social // This helper is exposed via electron_bridge for use in the social
// login flow. // login flow.

View File

@@ -1,5 +1,7 @@
import {remote, ContextMenuParams} from 'electron'; import {remote, ContextMenuParams} from 'electron';
import * as t from '../utils/translation-util'; import * as t from '../utils/translation-util';
const {clipboard, Menu} = remote; const {clipboard, Menu} = remote;
export const contextMenu = (webContents: Electron.WebContents, event: Event, props: ContextMenuParams) => { export const contextMenu = (webContents: Electron.WebContents, event: Event, props: ContextMenuParams) => {

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,13 @@
import {ipcRenderer, remote} from 'electron'; import {ipcRenderer, remote} from 'electron';
import path from 'path';
import fs from 'fs'; import fs from 'fs';
import path from 'path';
import * as ConfigUtil from '../utils/config-util'; import * as ConfigUtil from '../utils/config-util';
import * as SystemUtil from '../utils/system-util'; import * as SystemUtil from '../utils/system-util';
import BaseComponent from './base'; import BaseComponent from './base';
import handleExternalLink from './handle-external-link';
import {contextMenu} from './context-menu'; import {contextMenu} from './context-menu';
import handleExternalLink from './handle-external-link';
const {app, dialog} = remote; const {app, dialog} = remote;

View File

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

View File

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

View File

@@ -1,27 +1,29 @@
import {ipcRenderer, remote, clipboard} from 'electron'; import {ipcRenderer, remote, clipboard} from 'electron';
import {feedbackHolder} from './feedback';
import path from 'path'; import path from 'path';
import escape from 'escape-html';
import isDev from 'electron-is-dev'; 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 // eslint-disable-next-line import/no-unassigned-import
import './tray'; import './tray';
import * as DomainUtil from './utils/domain-util'; const {session, app, Menu, dialog} = remote;
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';
interface FunctionalTabProps { interface FunctionalTabProps {
name: string; name: string;
@@ -59,7 +61,14 @@ const logger = new Logger({
}); });
const rendererDirectory = path.resolve(__dirname, '..'); 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 { class ServerManagerView {
$addServerButton: HTMLButtonElement; $addServerButton: HTMLButtonElement;
@@ -590,19 +599,13 @@ class ServerManagerView {
// not crash app when this.tabs is passed into // not crash app when this.tabs is passed into
// ipcRenderer. Something about webview, and props.webview // ipcRenderer. Something about webview, and props.webview
// properties in ServerTab causes the app to crash. // properties in ServerTab causes the app to crash.
get tabsForIpc(): ServerOrFunctionalTab[] { get tabsForIpc(): TabData[] {
const tabs: ServerOrFunctionalTab[] = []; return this.tabs.map(tab => ({
this.tabs.forEach((tab: ServerOrFunctionalTab) => { role: tab.props.role,
const proto = Object.create(Object.getPrototypeOf(tab)); name: tab.props.name,
const tabClone = Object.assign(proto, tab); index: tab.props.index,
webviewName: tab.webview.props.name
tabClone.webview = {props: {}}; }));
tabClone.webview.props.name = tab.webview.props.name;
delete tabClone.props.webview;
tabs.push(tabClone);
});
return tabs;
} }
activateTab(index: number, hideOldTab = true): void { activateTab(index: number, hideOldTab = true): void {

View File

@@ -1,12 +1,14 @@
import {ipcRenderer} from 'electron'; import {ipcRenderer} from 'electron';
import MacNotifier from 'node-mac-notifier';
import electron_bridge from '../electron-bridge';
import * as ConfigUtil from '../utils/config-util';
import { import {
appId, customReply, focusCurrentServer, parseReply appId, customReply, focusCurrentServer, parseReply
} from './helpers'; } 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 ReplyHandler = (response: string) => void;
type ClickHandler = () => void; type ClickHandler = () => void;
let replyHandler: ReplyHandler; let replyHandler: ReplyHandler;

View File

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

View File

@@ -1,8 +1,8 @@
import {remote} from 'electron'; import {remote} from 'electron';
import electron_bridge from '../electron-bridge';
import {appId, loadBots} from './helpers';
import DefaultNotification from './default-notification'; import DefaultNotification from './default-notification';
import {appId} from './helpers';
const {app} = remote; const {app} = remote;
// From https://github.com/felixrieseberg/electron-windows-notifications#appusermodelid // From https://github.com/felixrieseberg/electron-windows-notifications#appusermodelid
@@ -67,7 +67,3 @@ export function newNotification(
actions: notification.actions 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 {remote, OpenDialogOptions} from 'electron';
import path from 'path'; import path from 'path';
import BaseComponent from '../../components/base'; import BaseComponent from '../../components/base';
import * as CertificateUtil from '../../utils/certificate-util'; import * as CertificateUtil from '../../utils/certificate-util';
import * as DomainUtil from '../../utils/domain-util'; import * as DomainUtil from '../../utils/domain-util';

View File

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

View File

@@ -1,12 +1,13 @@
import {ipcRenderer} from 'electron'; import {ipcRenderer} from 'electron';
import BaseSection from './base-section';
import * as DomainUtil from '../../utils/domain-util'; 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 * 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 { interface ConnectedOrgSectionProps {
$root: Element; $root: Element;
} }

View File

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

View File

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

View File

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

View File

@@ -1,14 +1,15 @@
import {ipcRenderer} from 'electron'; import {ipcRenderer} from 'electron';
import BaseComponent from '../../components/base'; 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 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; type Section = ServersSection | GeneralSection | NetworkSection | ConnectedOrgSection | ShortcutsSection;
export default class PreferenceView extends BaseComponent { export default class PreferenceView extends BaseComponent {

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
import {ipcRenderer, remote, WebviewTag, NativeImage} from 'electron'; import {ipcRenderer, remote, WebviewTag, NativeImage} from 'electron';
import path from 'path'; import path from 'path';
import * as ConfigUtil from './utils/config-util'; import * as ConfigUtil from './utils/config-util';
const {Tray, Menu, nativeImage, BrowserWindow} = remote; const {Tray, Menu, nativeImage, BrowserWindow} = remote;

View File

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

View File

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

View File

@@ -1,12 +1,13 @@
import {JsonDB} from 'node-json-db'; import {remote, ipcRenderer} from 'electron';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import Logger from './logger-util';
import {remote, ipcRenderer} from 'electron'; import {JsonDB} from 'node-json-db';
import * as Messages from '../../../resources/messages';
import * as EnterpriseUtil from './enterprise-util'; import * as EnterpriseUtil from './enterprise-util';
import * as Messages from '../../../resources/messages'; import Logger from './logger-util';
const {app, dialog} = remote; const {app, dialog} = remote;

View File

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

View File

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

View File

@@ -1,11 +1,12 @@
import {Console} from 'console'; // eslint-disable-line node/prefer-global/console import {Console} from 'console'; // eslint-disable-line node/prefer-global/console
import {initSetUp} from './default-util'; import electron from 'electron';
import {sentryInit, captureException} from './sentry-util';
import fs from 'fs'; import fs from 'fs';
import os from 'os'; import os from 'os';
import isDev from 'electron-is-dev'; import isDev from 'electron-is-dev';
import electron from 'electron';
import {initSetUp} from './default-util';
import {sentryInit, captureException} from './sentry-util';
interface LoggerOptions { interface LoggerOptions {
timestamp?: true | (() => string); timestamp?: true | (() => string);

View File

@@ -1,7 +1,9 @@
import {ipcRenderer} from 'electron'; import {ipcRenderer} from 'electron';
import type WebView from '../components/webview';
import backoff from 'backoff'; import backoff from 'backoff';
import type WebView from '../components/webview';
import Logger from './logger-util'; import Logger from './logger-util';
const logger = new Logger({ const logger = new Logger({

View File

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

View File

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

View File

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

Binary file not shown.

Binary file not shown.

View File

@@ -2,6 +2,25 @@
All notable changes to the Zulip desktop app are documented in this file. All notable changes to the Zulip desktop app are documented in this file.
### 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 ### v5.3.0 --2020-06-24
**Security fixes**: **Security fixes**:

View File

@@ -1,16 +1,16 @@
'use strict'; 'use strict';
const gulp = require('gulp'); const {execSync} = require('child_process');
const electron = require('electron-connect').server.create({ const electron = require('electron-connect').server.create({
verbose: true 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 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 baseFilePattern = 'app/+(main|renderer)/**/*';
const globOptions = {cwd: __dirname}; const globOptions = {cwd: __dirname};
const jsFiles = glob.sync(baseFilePattern + '.js', globOptions); const jsFiles = glob.sync(baseFilePattern + '.js', globOptions);

3318
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{ {
"name": "zulip", "name": "zulip",
"productName": "Zulip", "productName": "Zulip",
"version": "5.4.0", "version": "5.4.1-beta",
"main": "./app/main", "main": "./app/main",
"description": "Zulip Desktop App", "description": "Zulip Desktop App",
"license": "Apache-2.0", "license": "Apache-2.0",
@@ -50,7 +50,7 @@
"files": [ "files": [
"app/**/*" "app/**/*"
], ],
"copyright": "©2019 Kandra Labs, Inc.", "copyright": "©2020 Kandra Labs, Inc.",
"mac": { "mac": {
"category": "public.app-category.productivity", "category": "public.app-category.productivity",
"target": [ "target": [
@@ -146,8 +146,8 @@
], ],
"dependencies": { "dependencies": {
"@electron-elements/send-feedback": "^2.0.3", "@electron-elements/send-feedback": "^2.0.3",
"@sentry/electron": "^1.4.0", "@sentry/electron": "^1.5.0",
"@yaireo/tagify": "^3.15.3", "@yaireo/tagify": "^3.15.4",
"adm-zip": "^0.4.16", "adm-zip": "^0.4.16",
"auto-launch": "^5.0.5", "auto-launch": "^5.0.5",
"backoff": "^2.5.0", "backoff": "^2.5.0",
@@ -174,14 +174,14 @@
"@types/escape-html": "^1.0.0", "@types/escape-html": "^1.0.0",
"@types/fs-extra": "^9.0.1", "@types/fs-extra": "^9.0.1",
"@types/i18n": "^0.8.6", "@types/i18n": "^0.8.6",
"@types/node": "^14.0.23", "@types/node": "^14.0.25",
"@types/requestidlecallback": "^0.3.1", "@types/requestidlecallback": "^0.3.1",
"@typescript-eslint/eslint-plugin": "^3.6.1", "@typescript-eslint/eslint-plugin": "^3.7.0",
"@typescript-eslint/parser": "^3.6.1", "@typescript-eslint/parser": "^3.7.0",
"devtron": "^1.4.0", "devtron": "^1.4.0",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"electron": "^8.4.0", "electron": "^9.1.1",
"electron-builder": "^22.7.0", "electron-builder": "^22.8.0",
"electron-connect": "^0.6.3", "electron-connect": "^0.6.3",
"electron-notarize": "^1.0.0", "electron-notarize": "^1.0.0",
"glob": "^7.1.6", "glob": "^7.1.6",
@@ -192,7 +192,7 @@
"nodemon": "^2.0.4", "nodemon": "^2.0.4",
"pre-commit": "^1.2.2", "pre-commit": "^1.2.2",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"spectron": "^10.0.1", "spectron": "^11.1.0",
"stylelint": "^13.6.1", "stylelint": "^13.6.1",
"tap-colorize": "^1.2.0", "tap-colorize": "^1.2.0",
"tape": "^5.0.1", "tape": "^5.0.1",
@@ -204,6 +204,17 @@
"@typescript-eslint/no-dynamic-delete": "off", "@typescript-eslint/no-dynamic-delete": "off",
"@typescript-eslint/prefer-readonly-parameter-types": "off", "@typescript-eslint/prefer-readonly-parameter-types": "off",
"arrow-body-style": "error", "arrow-body-style": "error",
"import/first": "error",
"import/newline-after-import": "error",
"import/order": [
"error",
{
"alphabetize": {
"order": "asc"
},
"newlines-between": "always"
}
],
"import/unambiguous": "error", "import/unambiguous": "error",
"max-lines": [ "max-lines": [
"warn", "warn",

View File

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

View File

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

View File

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

View File

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

View File

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