Compare commits

...

1231 Commits

Author SHA1 Message Date
Anders Kaseorg
7fba8cfae9 release: New release v5.10.0.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-05 16:15:08 -07:00
Anders Kaseorg
32301656cc Upgrade dependencies, including Electron 24.2.0.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-05 16:15:04 -07:00
Anders Kaseorg
0e16283a37 stylelint: Fix declaration-block-no-redundant-longhand-properties.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-05 16:15:04 -07:00
Anders Kaseorg
d86482a804 stylelint: Fix media-feature-range-notation.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-05 16:15:04 -07:00
Anders Kaseorg
3af350e4dc translations: Update translations from Transifex.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-05 16:14:26 -07:00
Anders Kaseorg
39fc2053c5 translations: Update en.json.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-05 16:10:46 -07:00
Anders Kaseorg
044f1fd0f9 preference: Fix server icon display in connected organizations list.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-05 15:30:51 -07:00
Anders Kaseorg
10fb0a82f9 preload: Drop compatibility code for Zulip Server < 4.0.
The server was updated in bfd9999cf874e506592fda254dfe0fe06b5b2738
(4.0-rc1~2192) to expose a proper API for this functionality, so we
don’t need to trigger fake click events to access it.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-05 14:41:25 -07:00
Anders Kaseorg
123bd5b2c0 preload: Drop compatibility injected JS for Zulip Server < 3.0.
The server was updated in a6fee2f18ef9d2ef6ac248e9ed82d580daff1a07
(3.0-dev~1674) and e701f208619b8b9b28a85f84ee16cf8d8df82b72
(3.0-dev~1667) to avoid relying on this wrapper.  We no longer support
servers older than 3.0, so we can delete it.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-05 14:39:41 -07:00
Anders Kaseorg
ad771c3da8 Display a banner for unsupported Zulip Server versions.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-05 14:37:32 -07:00
Anders Kaseorg
4c58bc3aa3 webview: Add a wrapper pane around the real <webview>.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-05 14:35:14 -07:00
Anders Kaseorg
9a8680d209 webview: Use private methods.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-05 14:35:14 -07:00
Anders Kaseorg
1569890f4d webview: Use private members.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-05 14:35:14 -07:00
Anders Kaseorg
2ed400c23c webview: Add destroy method.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-05 14:35:14 -07:00
Anders Kaseorg
70621431dc translations: Update translations from Transifex.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-05 14:32:11 -07:00
Anders Kaseorg
55b7e09796 tx: Migrate configuration to current Transifex CLI.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-05 14:28:06 -07:00
Anders Kaseorg
de2829a968 translations: Update en.json.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-05 14:23:21 -07:00
Anders Kaseorg
296de41779 translation-util: Expose the full functionality of __.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-05 14:23:21 -07:00
Anders Kaseorg
8b9ebeee25 Fix more typos.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-05 14:23:21 -07:00
Anders Kaseorg
76e81ca337 Fix updating of server names and icons at startup.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-04 15:12:18 -07:00
Anders Kaseorg
2e7a9bb4ed server-tab: Encapsulate setName and setIcon.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-05-04 15:12:18 -07:00
Anders Kaseorg
77638f6287 Fix handling of server icon updates and errors.
Fixes #1283.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-04-21 15:37:28 -07:00
Anders Kaseorg
6e8fe36876 Fix typos.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-04-19 13:50:40 -07:00
Anders Kaseorg
2eea4a32a5 preference: Fix CSS in Vite dev mode.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-04-18 14:21:06 -07:00
Anders Kaseorg
677dfe425c xo: Remove redundant exclusion of unicorn/prefer-json-parse-buffer.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-04-18 14:03:45 -07:00
Anders Kaseorg
1da3ec545a Don’t show visual notifications when they’re turned off.
Fixes #1299.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-04-18 13:13:12 -07:00
Anders Kaseorg
3cb6ea4694 Handle exceptions when reading server icons.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-04-18 12:49:09 -07:00
Anders Kaseorg
0cb7297017 preference: Fix spellchecker languages dropdown positioning.
Apparently the Tagify defaults don’t work inside a shadow root.

Fixes #1286.  Closes #1290.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-04-04 15:24:22 -07:00
Anders Kaseorg
b8d7003446 Use Zod 3 style for importing Zod.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-03-04 00:23:00 -08:00
Anders Kaseorg
6d27cf8c7d release: New release v5.9.5.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-02-06 21:17:32 -08:00
Anders Kaseorg
1ac2483cc4 Upgrade dependencies, including Electron 22.2.0.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-02-06 21:14:43 -08:00
Anders Kaseorg
4d3420dcd0 vite: Externalize gatemaker.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-02-06 21:13:23 -08:00
Anders Kaseorg
38450a9aed vite: Don’t externalize dependencies.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-02-06 19:14:19 -08:00
Anders Kaseorg
24de7ebb97 webview: Remove did-navigate workaround
The Electron bug seems to have been fixed upstream.  Meanwhile, the
workaround had been causing the app to hang if it can’t connect to an
organization at startup.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-02-06 18:57:40 -08:00
Anders Kaseorg
5a571d66d0 Enable Chromium sandboxing for remote webviews.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-02-06 18:57:22 -08:00
Anders Kaseorg
0ae998a51e Move clipboard decryption to main process.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-02-06 18:57:22 -08:00
Anders Kaseorg
447dd18b8b Read injected.js from main process.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-02-06 18:57:22 -08:00
Anders Kaseorg
9a200dc40c Replace remote wrapper module with Vite alias.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-02-06 18:57:22 -08:00
Anders Kaseorg
d42b752ac1 Bundle with Vite.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-02-06 18:57:22 -08:00
Anders Kaseorg
2f4103248d Move icons and sounds to public/resources.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-02-06 18:57:22 -08:00
Anders Kaseorg
985d731d2b Move translations to public/translations.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-02-06 18:57:22 -08:00
Anders Kaseorg
032f95150c renderer: Add async constructors for functional tabs.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-02-06 18:57:22 -08:00
Anders Kaseorg
d1aa5778c3 renderer: Set the icon src to a data: URL.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-02-06 18:57:22 -08:00
Anders Kaseorg
13ce24b75e webview: Remove unnecessary __dirname resolution of customCss.
We’ve already checked that the file exists without resolving via
__dirname.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-02-06 18:57:22 -08:00
fwcd
c89ec2faf1 Update installation instructions for macOS 2023-02-01 21:50:19 -08:00
Anders Kaseorg
56ab0833b8 release: New release v5.9.4.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-01-04 16:29:48 -08:00
Anders Kaseorg
c62b393c52 Set quarantine attribute for downloads on macOS.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-01-04 16:12:31 -08:00
Anders Kaseorg
991de77cad Restore default macOS security settings.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-01-03 20:36:13 -08:00
Anders Kaseorg
94780c44c8 handle-external-link: Ignore invalid URLs.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-01-03 18:06:36 -08:00
Anders Kaseorg
82542a6390 packaging: Synchronize deb-after-install.sh with upstream.
https://github.com/electron-userland/electron-builder/blob/v23.6.0/packages/app-builder-lib/templates/linux/after-install.tpl

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-01-03 17:14:50 -08:00
Anders Kaseorg
53ff8443dc Upgrade dependencies, including Electron 22.0.0.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-01-03 16:17:24 -08:00
Anders Kaseorg
3855ecab58 Disable sandboxing for now.
Sandboxing will default to enabled in Electron ≥ 20, but we don’t
support it yet.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-01-03 16:17:24 -08:00
Anders Kaseorg
a57cbb4aa8 package.json: Bump engines to node ≥ 16.13.2.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-01-03 16:05:28 -08:00
Anders Kaseorg
56a4461c2a xo: Fix n/file-extension-in-import, maybe.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-01-03 16:05:28 -08:00
Anders Kaseorg
cd023ec5ab xo: Fix @typescript-eslint/consistent-type-definitions.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-01-03 16:05:28 -08:00
Anders Kaseorg
1aa4ade3c0 xo: Fix @typescript-eslint/parameter-properties.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-01-03 16:05:28 -08:00
Anders Kaseorg
dcb46eef4f xo: Fix @typescript-eslint/no-useless-empty-export.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-01-03 16:05:28 -08:00
Anders Kaseorg
e3e8ef6e3e xo: Fix @typescript-eslint/consistent-generic-constructors.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-01-03 16:05:28 -08:00
Anders Kaseorg
6808b1971a xo: Fix unicorn/switch-case-braces.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-01-03 16:05:28 -08:00
Anders Kaseorg
1dd5269549 xo: Fix unicorn/prefer-node-protocol.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-01-03 16:05:28 -08:00
Anders Kaseorg
d33adca1e8 xo: Fix unicorn/prefer-logical-operator-over-ternary.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-01-03 16:05:28 -08:00
Anders Kaseorg
8ea7f7864f autoupdater: Add const assertion.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-01-03 14:09:17 -08:00
Anders Kaseorg
493ae06e52 Reformat with Prettier.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-01-03 14:08:23 -08:00
Anders Kaseorg
2b8f3536d3 Fix E2E tests broken by chat.zulip.org web-public streams.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-12-14 22:41:35 -08:00
Anders Kaseorg
544d23ec09 how-to-install: Update APT instructions.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-07-15 15:36:41 -07:00
Anders Kaseorg
588d32fd22 release: New release v5.9.3.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-04-28 20:25:15 -07:00
Anders Kaseorg
1c471fe624 Upgrade dependencies, including Electron 18.2.0.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-04-28 20:06:46 -07:00
Anders Kaseorg
52486d687d Allow the autoupdater to quit the app normally.
Forcing it to quit would prematurely terminate the update on some
platforms.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-04-28 19:51:07 -07:00
Anders Kaseorg
73441d791c release: New release v5.9.2.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-04-20 21:23:00 -07:00
Anders Kaseorg
1bb6423721 Upgrade dependencies, including Electron 18.1.0.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-04-20 19:26:47 -07:00
Anders Kaseorg
d6775d64a3 release: New release v5.9.1.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-04-08 17:20:31 -07:00
Anders Kaseorg
e1326eae91 sentry: Update DSN.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-04-08 17:18:48 -07:00
Anders Kaseorg
b93955b28f Upgrade dependencies, including Electron 18.0.3.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-04-08 17:10:59 -07:00
Anders Kaseorg
e3452bda22 Simplify if (…) classList.add(…) else classList.remove(…) anti-pattern.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-04-02 14:34:58 -07:00
Anders Kaseorg
0aab691b44 Switch to released @electron/remote.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-04-01 21:04:51 -07:00
Anders Kaseorg
1bfb2dd975 release: New release v5.9.0.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-04-01 17:25:21 -07:00
Anders Kaseorg
fb7937314b Upgrade dependencies.
electron-builder@next is needed to build a DMG on macOS 12.3.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-04-01 14:24:22 -07:00
Anders Kaseorg
e39d2a9b95 xo: Fix unicorn/prefer-node-protocol.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-31 21:52:32 -07:00
Anders Kaseorg
3b04b61662 Upgrade dependencies, including Electron 18.0.1.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-31 21:21:21 -07:00
Anders Kaseorg
829b2a0f2a package-lock.json: Upgrade to lockfileVersion 2.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-31 21:13:41 -07:00
Anders Kaseorg
5edffbdf21 Move handleExternalLink to main process.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-31 21:10:13 -07:00
Anders Kaseorg
27576c95e6 Skip unnecessary remote for clipboard, nativeImage, shell.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-14 21:48:44 -07:00
Anders Kaseorg
5acc45cba4 Use process-specific electron/{main,renderer,common} imports.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-14 21:38:18 -07:00
Anders Kaseorg
343e0ed848 xo: Simplify configuration.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-14 20:47:40 -07:00
Anders Kaseorg
0c784b12fa WebView: Enable allowpopups.
This is required for Electron ≥ 15 to continue invoking our new window
handler (handleExternalLink), following the nativeWindowOpen
migration.

https://github.com/electron/electron/issues/30886

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-11 18:23:39 -08:00
Anders Kaseorg
2b50b21752 tsconfig: Downgrade target to ES2021.
The ES2022 definition of Error#cause conflicts with @types/verror.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-09 15:54:05 -08:00
Anders Kaseorg
ad604f020d tsconfig: Remove lib setting.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-09 15:09:31 -08:00
Anders Kaseorg
4151e020f6 Revert "xo: Fix import/extensions."
This reverts commit 5623ab3866.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-08 21:58:40 -08:00
Anders Kaseorg
bc59714192 xo: Fix @typescript-eslint/naming-convention.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-08 21:24:49 -08:00
Anders Kaseorg
b43a7b6809 xo: Fix unicorn/template-indent.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-08 21:15:32 -08:00
Anders Kaseorg
fba8aa0ab0 xo: Fix object-shorthand.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-08 21:15:32 -08:00
Anders Kaseorg
5623ab3866 xo: Fix import/extensions.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-08 21:15:32 -08:00
Anders Kaseorg
a4fbf9bd28 stylelint: Fix shorthand-property-no-redundant-values.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-08 21:15:32 -08:00
Anders Kaseorg
db730da45c stylelint: Ignore selector-id-pattern for #nav-AddServer.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-08 21:15:32 -08:00
Anders Kaseorg
b5a938d3b0 stylelint: Ignore selector-class-pattern for .__tagify_input.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-08 21:15:32 -08:00
Anders Kaseorg
863d1e25ba stylelint: Fix keyframes-name-pattern.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-08 21:15:32 -08:00
Anders Kaseorg
a90aaeb86c stylelint: Fix function-url-quotes.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-08 21:15:32 -08:00
Anders Kaseorg
8b6af78f2a stylelint: Fix font-family-name-quotes. 2022-03-08 21:15:32 -08:00
Anders Kaseorg
6c2dcb450b stylelint: Fix alpha-value-notation, color-function-notation. 2022-03-08 21:15:32 -08:00
Anders Kaseorg
f57962d02f .stylelintrc: Format with Prettier.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-08 21:15:32 -08:00
Anders Kaseorg
2983c381ae Fix Electron.Session type.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-08 21:15:32 -08:00
Anders Kaseorg
1ea7fa813a Remove redundant webPreferences defaults.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-08 21:15:22 -08:00
Anders Kaseorg
e434c5b5d0 Untangle Sentry initialization.
Thanks to upstream for the helpful advice at
https://github.com/getsentry/sentry-electron/issues/427.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-08 16:55:36 -08:00
Anders Kaseorg
9c1f47badd Move server manager view to the default session.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-08 16:55:23 -08:00
Anders Kaseorg
4ed4328bf8 Toggle spell checker in the session rather than the webPreferences.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-08 16:05:54 -08:00
Anders Kaseorg
c6022e94bb main: Enable contextIsolation for BrowserWindow.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-04 16:56:44 -08:00
Anders Kaseorg
06eb169c65 WebView: Restrict $el type to HTMLElement.
The extra methods on WebviewTag are not available from the
context-isolated preload script.
https://github.com/electron/electron/issues/26904

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-04 16:56:44 -08:00
Anders Kaseorg
2f7529cd71 WebView: Get event parameters via WebContents rather than WebviewTag.
Works around https://github.com/electron/electron/issues/31924.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-04 16:56:44 -08:00
Anders Kaseorg
3a8541f601 WebView: Call getWebContentsId in main world.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-04 16:56:44 -08:00
Anders Kaseorg
0eb910b2e8 WebView: Use send method.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-04 16:56:44 -08:00
Anders Kaseorg
76a879e4fd WebView: Convert WebviewTag methods to WebContents methods.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-04 16:56:44 -08:00
Anders Kaseorg
7026e43575 WebView: Add getWebContents method.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-04 16:56:44 -08:00
Anders Kaseorg
869361bac3 WebView: Type $el as required.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-04 16:56:44 -08:00
Anders Kaseorg
832ea3c04e WebView: Remove async from send method.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-04 16:56:44 -08:00
Anders Kaseorg
68232f966e WebView: Wait for did-navigate before constructing WebView.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-04 16:56:44 -08:00
Anders Kaseorg
86b7da45ef WebView: Use a better focus() workaround.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-04 16:56:44 -08:00
Anders Kaseorg
b853856317 WebView: Add factory function.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-04 16:56:44 -08:00
Anders Kaseorg
6676f1c6ac WebView: Switch templateHTML to a static method.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-04 16:56:44 -08:00
Anders Kaseorg
e0243bc460 main: Disable nodeIntegration for BrowserWindow.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-04 16:56:44 -08:00
Anders Kaseorg
fd6cb548f8 WebView: Remove nodeIntegration parameter.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-04 16:56:44 -08:00
Anders Kaseorg
743b2d6054 WebView: Make preload a string.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-04 16:56:44 -08:00
Anders Kaseorg
fb5c6b365e css: Simplify webview CSS.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-04 16:56:44 -08:00
Anders Kaseorg
f092e99f42 css: Remove the melodramatic fade-in animation on load.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-04 16:56:44 -08:00
Anders Kaseorg
751eb6ef98 Switch electron.remote to @electron/remote.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-03-04 16:56:44 -08:00
Anders Kaseorg
980de649e3 common: Factor out electron.remote pattern to a module.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-28 16:42:04 -08:00
Anders Kaseorg
84849d2c84 Move functional tab pages out of separate webviews.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-28 16:42:04 -08:00
Anders Kaseorg
b263997bed tray: Move initialization to a function.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-28 16:07:37 -08:00
Anders Kaseorg
12c773bc71 tray: Be robust in case there’s no active webview.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-28 16:07:33 -08:00
Anders Kaseorg
d937539618 renderer: Restrict webview functions to ServerTab instances.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-28 16:07:28 -08:00
Anders Kaseorg
0a5d07f839 renderer: Inline FunctionalTabProps type.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-28 16:07:23 -08:00
Anders Kaseorg
5dcd3956ac preference: Unify duplicate toggle-sidebar-setting event.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-28 16:07:21 -08:00
Anders Kaseorg
3ffc7251f4 preference: Unify duplicate toggle-menubar-setting event.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-28 16:06:09 -08:00
Anders Kaseorg
7fb0cfd176 WebView: Remove redundant name property.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-28 16:05:25 -08:00
Anders Kaseorg
5c83952ba1 webview: Remove forceLoad method.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-28 16:04:29 -08:00
Anders Kaseorg
a7a051bb2a renderer: Remove dead show-network-error message.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-28 16:04:21 -08:00
Anders Kaseorg
2b2c5dbe5c about: Encapsulate in a custom element.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-28 16:04:20 -08:00
Anders Kaseorg
ffe87a9729 preference: Encapsulate in a custom element.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-28 15:58:36 -08:00
Anders Kaseorg
b366195415 Upgrade playwright-core.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-17 22:57:19 -08:00
Anders Kaseorg
f9f2b20e90 preference: Use querySelector relative to $root.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-17 22:45:10 -08:00
Anders Kaseorg
e16811065d css: Extract font definitions to fonts.css.
This works around
https://bugs.chromium.org/p/chromium/issues/detail?id=336876.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-10 00:14:00 -08:00
Anders Kaseorg
f66a1127de electron-bridge: Remove console.log debugging spew.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-03 23:09:50 -08:00
Anders Kaseorg
06ef60c4c2 notification: Remove BaseNotification wrapper class.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-03 23:02:37 -08:00
Anders Kaseorg
4b93298b58 notification: Set the AppUserModelId from the main process.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-03 22:55:15 -08:00
Anders Kaseorg
a41a771923 notification: Don’t use remote for focusCurrentServer.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-03 22:54:38 -08:00
Anders Kaseorg
a43f7d9bcf Fix glob usage in package scripts.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2022-02-03 03:02:07 -08:00
Anders Kaseorg
c9453f877b config-schemata: Remove unused systemProxyRules setting.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-11-23 17:55:32 -08:00
Anders Kaseorg
525fa94b18 Fix system proxy resolution.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-11-23 17:51:51 -08:00
Anders Kaseorg
460b9e5e55 main: Remove dead code for recreating main window.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-11-23 16:12:15 -08:00
Anders Kaseorg
8fc41a7ca8 system-util: Remove getOS wrapper.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-11-22 15:56:58 -08:00
Anders Kaseorg
4c7b9cf4e3 server-tab: Delete space in macOS shortcut text.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-11-22 15:50:32 -08:00
Anders Kaseorg
f4479dfda4 tests: Migrate E2E tests to Playwright.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-11-19 15:50:16 -08:00
Anders Kaseorg
377f08ad5d Fix unread count parsing from page title.
Fixes #1157

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-10-27 16:42:46 -07:00
Anders Kaseorg
add43bafda Fix ‘npm run prettier-non-js’ on Windows.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-10-10 23:15:09 -07:00
Anders Kaseorg
b35d45955b WebView: Move initialization from dom-ready event to did-attach event.
This fixes the bug where the context menu would disappear immediately
if the page had been loaded an even number of times.

Fixes #662, fixes #991, fixes #1010.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-10-08 18:10:43 -07:00
Anders Kaseorg
2ecb970da0 Revert "webview: fix focus after soft reload."
This reverts commit 6b98a49245 (#698).

The bug it worked around was fixed upstream in Electron 9.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-10-08 16:32:10 -07:00
Anders Kaseorg
edb2933dad Remove .prettierignore.non-js.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-10-06 16:13:47 -07:00
Anders Kaseorg
8141927974 tests: Remove dynamic package.json generation.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-10-06 16:07:29 -07:00
Anders Kaseorg
4db89ac3a7 typescript: Enable noImplicitOverride.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-09-10 21:52:32 -07:00
Anders Kaseorg
feb67e6c2d Deglobalize ElectronBridge type.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-09-01 14:04:51 -07:00
Anders Kaseorg
014e97b563 Remove feedback widget.
@electron-elements/send-feedback won’t work with Electron 14, and all
it ever did was open your mail client.  Have the “Report an Issue”
menu item direct users to our website instead.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-08-30 19:04:20 -07:00
Anders Kaseorg
a3f4e19aa2 autoupdater: Avoid deprecated log.FileTransport.file.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-08-30 14:18:31 -07:00
Anders Kaseorg
90a65ab6cc release: New release v5.8.1.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-29 19:26:30 -07:00
Anders Kaseorg
c00e1618e7 Downgrade electron-updater to 4.3.5.
Newer electron-updater versions are broken on macOS by
https://github.com/electron-userland/electron-builder/issues/5935 as
well as another issue that has not yet been diagnosed.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-29 19:26:30 -07:00
Anders Kaseorg
ceb6417979 Replace Linux zip build with tar.xz.
The filename of the Linux zip now conflicts with the macOS zip needed
by the auto-updater, and zip isn’t a usual format for Linux anyway.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 23:53:40 -07:00
Anders Kaseorg
1d40ebb65f release: New release v5.8.0.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 17:10:10 -07:00
Anders Kaseorg
6301427ef4 Fix Windows MSI filename.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 17:06:31 -07:00
Anders Kaseorg
64d1d6c88d Build arm64 pkg for macOS.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 16:33:37 -07:00
Anders Kaseorg
adcacd7d45 Tighten tab role type.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 15:46:40 -07:00
Anders Kaseorg
b6729b0d0a menu: Skip missing elements of tabs array.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 15:31:14 -07:00
Anders Kaseorg
ec7d5b4046 Upgrade dependencies, including Electron 13.1.7.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 13:45:09 -07:00
Anders Kaseorg
380ea3a891 tests: Add extension to .js imports.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 13:45:09 -07:00
Anders Kaseorg
320e152897 xo: Fix unicorn/numeric-separators-style.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 13:45:09 -07:00
Anders Kaseorg
c00d0abe0d enterprise-util: Use zod for type-safe validation.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 13:45:09 -07:00
Anders Kaseorg
aaa83da0f8 config-util: Use zod for type-safe validation.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 13:45:09 -07:00
Anders Kaseorg
494e716dfe domain-util: Use zod for type-safe validation.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 13:45:09 -07:00
Anders Kaseorg
50c266295e linux-update-util: Strongly type update items.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 13:45:06 -07:00
Anders Kaseorg
55a6122a6c general-section: Use zod for type-safe validation.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 13:37:31 -07:00
Anders Kaseorg
2a648b79c9 linuxupdater: Use zod for type-safe validation.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 13:37:31 -07:00
Anders Kaseorg
0bc49bf723 request: Use zod for type-safe validation.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 13:37:31 -07:00
Anders Kaseorg
cb7d1faa52 main: Annotate permissionCallbacks.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 13:37:31 -07:00
Anders Kaseorg
fa3c744e76 displayInitialCharLogo: Fix incorrect cast.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 13:37:31 -07:00
Anders Kaseorg
54be4dccce injected: Specify explicit type for cast.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 13:37:31 -07:00
Anders Kaseorg
6a407d0e42 preload: Fix weird event.target usage.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-21 13:37:31 -07:00
Anders Kaseorg
47171fffd5 Fix spell checker on macOS.
Although ses.setSpellCheckerLanguages is documented as a no-op on macOS,
ses.setSpellCheckerLanguages([]) actually disables spell checking as of
Electron 8.1.0 (https://github.com/electron/electron/issues/30215).
This effect is persistent in our persistent session, so we attempt to
undo it by copying the language list from the main BrowserWindow.

(Before commit 892f7c8e47 we were running
ses.setSpellCheckerLanguages(null), which just crashed with “TypeError:
Error processing argument at index 0, conversion failure from null”.)

Fixes #1132.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-20 16:47:05 -07:00
Anders Kaseorg
e48c9067a3 Upgrade Prettier to 2.3.2.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-07-05 16:55:47 -07:00
Anders Kaseorg
1d30c83f7a Revert "Added log-out shortcut"
This reverts commit 2a477abe5f.

This is not a common operation that needs a keyboard shortcut, and
it’s too easy to invoke by accident.  Fixes #1115.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-05-24 11:31:09 -07:00
Anders Kaseorg
9f76fb295e Remove color profile override.
Modern Chromium and Electron do color management correctly.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-05-02 14:16:10 -07:00
Anders Kaseorg
07e2ebe340 release: New release v5.7.0.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-30 21:33:50 -07:00
Anders Kaseorg
884d5e0e16 Upgrade dependencies, including Electron 12.0.6.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-30 21:30:36 -07:00
Anders Kaseorg
a3a79534ab Build for arm64 on macOS.
Fixes #1076.

For now we only build a DMG and zip for arm64, because the arm64 pkg
would overwrite the x64 pkg
(https://github.com/electron-userland/electron-builder/issues/5847).

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-30 21:19:34 -07:00
Anders Kaseorg
6acf1d3411 package-lock.json: Restore mysterious optional node-addon-api dependency.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-30 18:28:49 -07:00
Anders Kaseorg
e32480abfb package-lock.json: Upgrade various SHA-1 hashes to SHA-512.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-28 20:29:40 -07:00
Anders Kaseorg
07e7251d7d Upgrade dependencies, including Electron 12.0.5.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-28 20:29:40 -07:00
Anders Kaseorg
753b244630 tests: Convert if chain to switch statement.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-28 20:29:40 -07:00
Anders Kaseorg
892f7c8e47 Strongly type config options.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-28 20:29:40 -07:00
Anders Kaseorg
6a3f50d606 Use Partial types for DNDSettings, SettingsOptions.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-28 18:00:15 -07:00
Anders Kaseorg
a49cb77840 Skip startup code if another instance is running.
Commit 171d88755c removed a top-level
‘return’ statement, thereby allowing the app to proceed with running
some of its startup code even if it failed to grab the single-instance
lock.  Under some circumstances, this seems to result in an orphaned
background process constantly consuming CPU cycles.

Fixes #1102.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-26 19:02:51 -07:00
Anders Kaseorg
79f9362736 Strongly type IPC messages.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-25 20:44:05 -07:00
Anders Kaseorg
164038ec3c Specialize the renderer-callback event to permission-callback.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-25 20:39:49 -07:00
Anders Kaseorg
eacd52fb6c preference: Rename one of the two different toggletray messages.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-25 20:06:30 -07:00
Anders Kaseorg
20295ddc50 webview: Use send method.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-25 19:57:38 -07:00
Anders Kaseorg
994c412bd2 renderer: Use ipcRenderer.sendTo to communicate with other WebContents.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-25 19:57:38 -07:00
Anders Kaseorg
3b3fa88c89 electron-bridge: Check types of all event parameters.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-25 19:57:38 -07:00
Anders Kaseorg
afec96025b general-section: Remove some casts.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-25 19:57:38 -07:00
Anders Kaseorg
57b6144e7f Update dmg background.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-20 20:03:00 -07:00
Anders Kaseorg
9bff18ece3 Add dmg icon.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-20 20:02:46 -07:00
tarun8718
e89f44c87f css: Remove irregular shortcut highlighting.
Fixes #1104.

Signed-off-by: tarun8718 <tarunkumar8718@gmail.com>
2021-04-15 20:09:05 -07:00
Anders Kaseorg
99fe94fbab Upgrade dependencies, including Electron 12.0.4.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-15 20:00:30 -07:00
Anders Kaseorg
e0c2f43b2b xo: Enable import/no-cycle.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-15 19:49:32 -07:00
Tim Abbott
0c81eb93b9 docs: Create FUNDING.yml. 2021-04-08 06:32:09 -07:00
Anders Kaseorg
391c515779 injected: Use WeakMap instead of Symbol for attributeListener.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-03 15:38:52 -07:00
Anders Kaseorg
077f3e6e78 eslint: Enable sort-imports for member sorting.
This sorts the members imported within each individual declaration; we
use import/order for sorting multiple declarations.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-03 15:07:40 -07:00
Anders Kaseorg
3b2256bcec Fix Prettier formatting.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-03 00:47:53 -07:00
ashishbinu
87f5fa049f Fix vertical alignment of input placeholder 2021-04-03 12:37:52 +05:30
ashishbinu
5774b8a67b Fix input placeholder overflow in general settings 2021-04-03 12:37:52 +05:30
Anders Kaseorg
80fe51702b typescript: Use DefinitelyTyped declarations for @yaireo/tagify.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 18:20:21 -07:00
Anders Kaseorg
303ec73fa8 Move stylelint config packages to devDependencies.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 18:12:17 -07:00
Anders Kaseorg
815d9d4e28 typescript: Remove skipLibCheck.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 16:39:41 -07:00
Anders Kaseorg
6044b6328d tsconfig: Remove unused options.
noImplicitAny, noImplicitThis, and alwaysStrict are implied by strict.
noUnusedLocals, noUnusedParameters, and noImplicitReturns are handled
by xo.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 16:23:11 -07:00
Anders Kaseorg
9a81ade1c8 typescript: Enable strictNullChecks.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 15:19:42 -07:00
Anders Kaseorg
33c21d0153 ReconnectUtil: Inline clearState method.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 15:17:31 -07:00
Anders Kaseorg
1735ce6a8a tray: Remove unused return value from destroytray handler.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 15:15:36 -07:00
Anders Kaseorg
385ec00640 PreferenceNav: inline init method.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 15:08:40 -07:00
Anders Kaseorg
4f58a2a357 electron_bridge: Make the options argument of new_notification required.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 15:06:38 -07:00
Anders Kaseorg
1e10a3c406 domain-util: Make alias and icon required members of ServerConf.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 15:01:38 -07:00
Anders Kaseorg
ff3ea429d2 context-menu: Fix strictNullChecks violations.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 14:29:14 -07:00
Anders Kaseorg
bdf7d1b813 index: Fix strictNullChecks violations.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 14:28:55 -07:00
Anders Kaseorg
ae4b3a4778 Explode PreferenceView class to a function.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 14:28:52 -07:00
Anders Kaseorg
66bc43674b Explode ShortcutsSection class to a function.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 14:28:11 -07:00
Anders Kaseorg
8f0f82f98e Explode NetworkSection class to a function.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 14:28:09 -07:00
Anders Kaseorg
43f0b4c902 Explode ConnectedOrgSection class to a function.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 14:27:47 -07:00
Anders Kaseorg
867bb61e0d Explode GeneralSection class to a function.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 14:26:53 -07:00
Anders Kaseorg
10912fe270 Explode ServersSection class to a function.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 14:26:47 -07:00
Anders Kaseorg
76d9c36426 Explode NewServerForm class to a function.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 14:26:20 -07:00
Anders Kaseorg
6ca4d77b8f Explode ServerInfoForm class to a function.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 14:25:59 -07:00
Anders Kaseorg
7b3d40ca1e Explode FindAccounts class to a function.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 14:25:41 -07:00
Anders Kaseorg
ea2d84e810 Explode BaseSection class to functions.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 14:14:48 -07:00
Anders Kaseorg
e477aed8ff generateNodeFromHTML: Check for null.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 14:14:07 -07:00
Anders Kaseorg
07ffb09391 Explode BaseComponent class to a function.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-02 13:10:15 -07:00
Anders Kaseorg
54f02c9616 PreferenceView: Lift init() calls into switch.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-01 18:45:55 -07:00
Anders Kaseorg
eeade47eb6 nav: Add NavItem literal type.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-01 18:45:55 -07:00
Anders Kaseorg
8ec9a98c86 ServerTab: Inline init method.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-01 18:13:30 -07:00
Anders Kaseorg
277d7ef824 FunctionalTab: Inline init method.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-01 18:11:55 -07:00
Anders Kaseorg
cc844e6905 Logger: Remove unused options.
All of our loggers have timestamp: true.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-01 14:06:43 -07:00
Anders Kaseorg
9b550d6e4f Logger: Remove dynamic method assignment.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-01 14:06:43 -07:00
Anders Kaseorg
9c25807b99 Avoid use of null when accessing electron.remote.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-01 14:06:43 -07:00
Anders Kaseorg
b07995c3ed Simplify querySelectorAll(…)[0] to querySelector(…).
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-01 14:06:43 -07:00
Anders Kaseorg
67228d295d Reformat all code with Prettier.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-31 20:04:00 -07:00
Anders Kaseorg
4521041619 Enable stylelint-config-standard.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-31 20:03:06 -07:00
Anders Kaseorg
ce9a680333 Add a tagged template function for HTML supporting HTML interpolation.
This allows better Prettier integration: Prettier recognizes and
reformats tagged template literals with a tag named ‘html’.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-31 20:00:49 -07:00
Anders Kaseorg
2c40843306 Remove zulip-electron-launcher.sh.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-31 17:59:32 -07:00
Anders Kaseorg
8d3dad234e Remove .node-version.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-31 17:59:32 -07:00
Anders Kaseorg
6da7cf6b8e Remove Gulp build system.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-31 17:59:32 -07:00
tarun8718
25d0aefe37 server-tab: Fix upper limit of generated shortcuts
Signed-off-by: tarun8718 <tarunkumar8718@gmail.com>
2021-03-29 20:23:06 -07:00
Anders Kaseorg
afb25d5b3d Upgrade dependencies, including Electron 12.0.2.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-29 19:58:26 -07:00
Anders Kaseorg
78ae1b34ab Revert GDK_BACKEND to work around Electron shell.openExternal bug.
https://github.com/electron/electron/issues/28436

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-29 19:52:18 -07:00
Anders Kaseorg
d3401cc87e xo: Fix @typescript-eslint/type-annotation-spacing.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-29 18:01:52 -07:00
Anders Kaseorg
9cbe4fdb4a xo: Enable import/no-restricted-paths.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-29 18:01:52 -07:00
Anders Kaseorg
851bb7904f Move modules shared between main and renderer processes to app/common.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-29 18:01:52 -07:00
Anders Kaseorg
d7598d3091 Replace LinkUtil.openBrowser in main process with shell.openExternal.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-29 18:01:15 -07:00
Anders Kaseorg
76d321fa79 xo: Enable @typescript-eslint/consistent-type-imports.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-29 17:48:54 -07:00
Anders Kaseorg
c42aafe0b9 request: Use a stable URL for the isOnline check.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-29 13:31:08 -07:00
Anders Kaseorg
28db945b91 Remove obsolete troubleshooting guide
None of these issues are current.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-29 12:20:19 -07:00
Saksham Agarwal
a805e260c4 docs: Fix keyboard shortcuts for Electron developer console. 2021-03-24 15:12:53 -07:00
Anders Kaseorg
6e95e5439b Upgrade dependencies, including Electron 11.3.0.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-08 23:14:00 -08:00
Anders Kaseorg
a9f479d60d Enable worldSafeExecuteJavaScript.
This has no effect on our use of executeJavaScript, and will become
the default in Electron 12, but for now it silences a warning in
development mode.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-08 23:14:00 -08:00
Anders Kaseorg
7b095a683c Replace electron-is-dev with app.isPackaged.
This unfortunately uses remote, but that’s what electron-is-dev was
doing anyway.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-08 23:14:00 -08:00
Anders Kaseorg
78931bbb22 xo: Fix unicorn/no-array-push-push.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-08 23:13:50 -08:00
Anders Kaseorg
12ae84b757 xo: Fix unicorn/prefer-spread.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-08 23:13:43 -08:00
Anders Kaseorg
75da0a16c1 xo: Fix unicorn/no-array-for-each.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-03-08 23:13:36 -08:00
Anders Kaseorg
2a0f9b30e6 release: New release v5.6.0.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-02-16 15:55:16 -08:00
Anders Kaseorg
109795ca3e package: Migrate APT repository from Bintray to our domain.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-02-16 15:40:26 -08:00
Anders Kaseorg
e6e5e8a311 Upgrade dependencies, including Electron 11.2.3.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-02-16 15:40:04 -08:00
Anders Kaseorg
bd0869ec07 preload: Move extra keyboard shortcuts to invisible menu items.
Fixes #1060.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-02-16 15:29:25 -08:00
tarun8718
07ae127cc8 shortcuts-section: Deduplicate templateMacHTML and templateWinLinHTML.
Signed-off-by: tarun8718 <tarunkumar8718@gmail.com>
2021-02-02 10:49:35 -08:00
Anders Kaseorg
baa76c3244 Upgrade dependencies, including Electron 11.2.1.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-01-25 11:35:02 -08:00
Anders Kaseorg
7ac31f80ed xo: Remove unused @typescript-eslint/prefer-readonly-parameter-types override.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-01-25 11:35:02 -08:00
Anders Kaseorg
a95ee64f7d xo: Use eslint-import-resolver-typescript.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-01-25 11:35:02 -08:00
Anders Kaseorg
7d6c6bc10a xo: Fix unicorn/prefer-number-properties.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-01-25 11:23:48 -08:00
Anders Kaseorg
18b41938de xo: Fix unicorn/empty-brace-spaces.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-01-25 11:23:48 -08:00
Anders Kaseorg
9fe382b27f xo: Fix unicorn/explicit-length-check.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-01-25 11:23:48 -08:00
Anders Kaseorg
f022b338e6 xo: Fix unicorn/no-lonely-if.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-01-25 11:23:48 -08:00
Anders Kaseorg
855d99dfa0 xo: Fix unicorn/prevent-abbreviations.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-01-25 11:23:48 -08:00
Anders Kaseorg
cc2424e0bf xo: Fix @typescript-eslint/no-confusing-void-expression.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-01-25 11:23:48 -08:00
Anders Kaseorg
fa6d72268f Rename master branch to main.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-01-22 12:13:29 -08:00
Anders Kaseorg
762dd92ec3 Upgrade dependencies, including Electron 11.1.0.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-12-17 17:20:32 -08:00
Anders Kaseorg
2e90e24552 Remove fs-extra dependency.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-12-17 17:20:32 -08:00
Anders Kaseorg
d7adce0ebf appveyor: Use current dependencies.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-12-17 17:20:32 -08:00
Anders Kaseorg
a1bb6da4fb Switch Travis CI to GitHub Actions.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-12-16 18:45:20 -08:00
Anders Kaseorg
873fecf548 Revert "performance: Disable hardware acceleration to decrease the load on GPU."
This reverts commit fb74251a2c.

The actual problem in #213 was the infinite bouncing question mark
hotspot animation (https://github.com/zulip/zulip/issues/13760).

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-12-16 14:14:12 -08:00
Anders Kaseorg
682511bb68 injected: Remove unused page_params type declaration.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-12-08 16:40:49 -08:00
Anders Kaseorg
02fbe1a6a1 Revert to upstream undo and redo roles.
This reverts part of commit 01f6e77237
(#866).  The Electron bug was fixed upstream in Electron 9.0.0-beta.23.

Closes #899.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-12-02 19:54:22 -08:00
Anders Kaseorg
0cb82a6f5e release: New release v5.5.0.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-12-01 19:53:20 -08:00
Anders Kaseorg
79808e8ee9 preload: Provide hooks for server to robustly replace logout et al.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-12-01 18:11:45 -08:00
Anders Kaseorg
2c38df10c8 electron-bridge: Expose boolean return from emit.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-12-01 17:59:59 -08:00
Anders Kaseorg
1ca15d44a0 electron-bridge: Move mutable state out of electron_bridge.
Only the initial value of a mutable field is exposed via
exposeInMainWorld, which is why we have a bunch of setter and getter
functions.  It’s better to avoid the possibility for this confusion.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-12-01 17:54:21 -08:00
Anders Kaseorg
82450a91a9 preload: Remove retry button redirection hack.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-12-01 17:18:09 -08:00
Anders Kaseorg
62edfa6f8b Remove macOS notification inline replies feature.
node-mac-notifier no longer builds on macOS with Electron 11 (error:
no template named 'remove_cv_t' in namespace 'std').  It was
previously implicated in crashes on macOS (#1016).  And we no longer
have any macOS developers that seem to be maintaining this
feature (e.g. #1022 is stalled).

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-12-01 17:06:11 -08:00
Anders Kaseorg
fe86315ece main: Be explicit about disabling contextIsolation for the main window.
We have been relying on the default here, but the default will be
changing in Electron 12.  (We already enable contextIsolation in the
webviews that load remote content.)

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-12-01 16:34:41 -08:00
Anders Kaseorg
df3f719e89 Upgrade dependencies, including Electron 11.0.3.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-12-01 16:02:41 -08:00
Anders Kaseorg
0632d8199f injected: Condition narrow-by-topic handler on page_params.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-12-01 16:02:32 -08:00
Anders Kaseorg
047bf0ca45 webview: Pass webPreferences values as explicit booleans
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-11-30 12:39:35 -08:00
Anders Kaseorg
356c879668 Remove Devtron.
Devtron is unmaintained and no longer works.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-11-18 15:25:33 -08:00
Anders Kaseorg
ba432d32b3 Remove preventdrag script.
This was not a security feature; security is enforced using context
isolation and the same-origin policy.

Furthermore, navigation on drag-and-drop was already disabled by
default in Electron 3.0.

https://www.electronjs.org/blog/electron-3-0#breaking-api-changes

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-11-17 16:10:47 -08:00
Anders Kaseorg
c8ada3f47d Rewrite reinstall script to avoid auxilliary script files.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-11-17 15:41:46 -08:00
aryanshridhar
cd77fc6448 new-server-form: Strip whitespace from added organization URL.
Fixes #1037.
2020-11-15 19:56:53 -08:00
Anders Kaseorg
a2f926c611 README: Migrate Travis badge to travis-ci.com.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-10-27 15:49:22 -07:00
Anders Kaseorg
6c5eb85a16 README: Use Markdown for screenshot display.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-10-27 15:45:55 -07:00
Anders Kaseorg
cadb1c6eaa Upgrade dependencies, including Electron 10.1.5.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-10-24 15:47:13 -07:00
Anders Kaseorg
73710319e6 xo: Fix unicorn/prevent-abbreviations.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-10-24 15:47:13 -07:00
Anders Kaseorg
da91dc5595 xo: Fix @typescript-eslint/consistent-indexed-object-style.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-10-24 15:47:13 -07:00
Anders Kaseorg
31d5e5a092 xo: Fix unicorn/prefer-ternary, I guess.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-10-24 15:47:13 -07:00
Anders Kaseorg
13ee1d0990 logger-util: Add missing space.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-10-24 15:47:13 -07:00
Anders Kaseorg
d5a9063378 typescript: Fix implicit any in catch clauses.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-10-24 15:47:13 -07:00
Anders Kaseorg
918064f35d checkDomain: Remove special handling for “certificate” error strings.
The fragile check has been broken by changing strings, and the default
invalidZulipServerError message is fine.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-10-24 15:47:01 -07:00
Anders Kaseorg
193b8326bc injected: Check if narrow is defined.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-10-24 15:32:05 -07:00
Anders Kaseorg
9abb7f376e injected: Remove unused default_language from zulipWindow type.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-10-24 15:32:05 -07:00
Anders Kaseorg
ac338fa438 Upgrade dependencies, including Electron 10.1.3.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-10-01 15:54:59 -07:00
Anders Kaseorg
f5b78ee845 Set enableRemoteModule.
We would like to disable the remote module for improved sandboxing
(#915), but until then this is required for Electron 10, which
disables the remote module by default.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-10-01 15:54:59 -07:00
Aryan Shridhar
126bb26a6e Tray Icon : Changed Unread tray icon in Windows.
Replaced unread messages icon in the lower tray bar in windows with a new icon.
Fixed #506.
2020-09-17 16:07:23 +05:30
Anders Kaseorg
23e86abb5b Remove support for custom certificate exceptions.
Version 5.4.0 and later uses electron.net for all network
requests (#993), so custom certificates can now be configured in the
same system certificate store that Chrome uses.

https://zulip.com/help/custom-certificates#desktop

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-09-11 22:25:28 -07:00
Anders Kaseorg
3a3714787f main: Fix mainWindowState scope.
Fixes a regression with the factory reset function introduced by
commit cf9d0c8aa2.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-09-10 21:11:41 -07:00
Anders Kaseorg
bc57aabc97 Disable unused Chromium plugins; delete old commented PDF code.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-09-10 18:28:35 -07:00
Anders Kaseorg
08df02a1ea changelog: Update for 5.4.3 release.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-09-09 23:18:59 -07:00
Akash Nimare
35ad6fbad0 release: New release v5.4.3. 2020-09-09 12:24:43 +05:30
Anders Kaseorg
97f8fe71af Escape all strings inserted into CSS selectors.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-09-04 22:52:42 -07:00
Anders Kaseorg
a9d59b3dcd CVE-2020-24582: Escape all strings interpolated into HTML.
Also fix various variable names to consistently indicate which strings
contain HTML.

Some of these changes close cross-site scripting vulnerabilities, and
others are for consistency.  It’s important to be meticulously
consistent about escaping so that changes that would introduce
vulnerabilities stand out as obviously wrong.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-09-04 22:52:38 -07:00
Anders Kaseorg
b7240e1c40 Upgrade dependencies, including Electron 9.3.0.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-09-03 17:00:51 -07:00
Anders Kaseorg
62aa849657 Upgrade dependencies, including Electron 9.2.1.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-08-25 15:42:06 -07:00
Anders Kaseorg
c302ebe282 general-section: Convert .filter(…)[0] to .find(…).
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-08-25 15:40:40 -07:00
Anders Kaseorg
6404bed519 tests: Fix E2E tests for Spectron 11.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-08-25 15:37:06 -07:00
Manav Mehta
8d4d168988 Update changelog.md for release 5.4.2 (#1017) 2020-08-12 23:18:38 +05:30
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
Akash Nimare
596561b731 release: New release v5.3.0. 2020-06-24 14:47:54 +05:30
Akash Nimare
5943c21814 Update electron to v8.3.3. 2020-06-21 01:06:05 +05:30
Akash Nimare
2456bba5ae translations: Sync translations from transifex. 2020-06-20 23:06:50 +05:30
Manav Mehta
0fff6336c7 spellchecker: Use Electron 8 built-in spellchecker.
* Using electron built-in spellchecker
* Added the custom context menu

Co-authored-by: Anders Kaseorg <anders@zulipchat.com>

Fixes: #504
2020-06-18 18:14:23 +05:30
Samyak Gaur
4261874e29 docs: Update issue template.
Explanation lines commented as the titles are self explanatory.
2020-06-17 14:50:45 +05:30
Akash Nimare
f976270d33 lint: Fix linting errors. 2020-06-17 01:44:08 +05:30
Samyak Gaur
4782f1cfd3 Docs: Update issue template.
Fixes: #926.
2020-06-17 00:20:20 +05:30
Akash Nimare
2f70621255 windows: Turn off start at login by default.
Fixes: #960.
2020-06-16 20:10:23 +05:30
Manav Mehta
b6f4e2b83c zoom: Fix zoom issues.
The zoom issues seemed to be caused due to electron roles.
We also had our own definitions firing up along with the roles which might be clashing and the other instances of roles not being much effective in our use case.

* Remove roles from zoom options
* Add accelerator for zoomIn

Fixes: #911.
2020-06-13 12:59:43 +05:30
Manav Mehta
25f4c3aba8 translations: Prevent i18n to automatically add strings.
Now that we have moved to Transifex, auto updation of translation strings is not required.
We would only need to update the resource file (en.json) and push it to Transifex and pulling the rest <lang>.json
2020-06-12 13:18:04 +05:30
Anders Kaseorg
1b23468375 Update zulipchat.com emails to zulip.com.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-06-08 14:02:08 -07:00
Anders Kaseorg
5d775405d9 Update zulipchat.com links to zulip.com.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-06-08 14:01:40 -07:00
Akash Nimare
7f13d9162a mac: Support pkg installer on macOS. 2020-06-05 23:26:10 +05:30
Anders Kaseorg
e0013c22ff package-lock.json: Update.
Unclear why npm wants to change this now.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-31 02:13:55 -07:00
Samyak Gaur
5c41afdccd window: Responsive fixes for main window.
Fixes: #963.
2020-05-27 13:23:40 +05:30
Anders Kaseorg
99a3530238 xo: Add arrow-body-style rule
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-26 11:25:24 -07:00
Manav Mehta
a5ceffc856 transifex: Setup Transifex for better translation process. 2020-05-20 22:11:13 +05:30
Manav Mehta
f55570f2f5 Handle Reset options gracefully.
Fixes: #903.
2020-05-20 21:54:36 +05:30
Manav Mehta
4f890c0316 macOS: Use electron API to get dark tray icon for the light theme. 2020-05-19 21:03:13 +05:30
Priyank Patel
bf651dece0 report-issue: Remove unnessacary instructions from the placeholder.
It looks like the removed instructions were directly copied from GitHub
issue template and did not fit here. Specifically, the first point about
including the platform is automatically included in the email we prepare for
the user and we also include browser and electron version. Then the second
bullet point re-iterates the instructions present. Lastly, the third point
asks for screenshot however user cannot add screenshot in the UI but they can
in the email.

We also remove an comment disabling a eslint rule. The rule was disable for no
reason because we should have used template literal instead of the undocumented
multi-str format which is not cleaner.
2020-05-19 11:02:39 -04:00
Priyank Patel
1babd8da42 report-issue: Darken the color of report issue button.
This is done so it compliments the newly added cancel button.
This change empahsizes the report issue button rather than the
cancel button.
2020-05-19 11:02:39 -04:00
Priyank Patel
bfd146f2d9 send-feedback: Move custom css to a css file.
It just makes it a bit cleaner and easier to update css.
During this transition we update the color hex values to rgb
because of stylelint.
2020-05-19 11:02:39 -04:00
Sanskar Bajpai
3126510245 report-issue: Add a cancel button to the modal.
A user found the lack of a cancel button in the UI a bad
experience. Previously, we would close it when the overlay
was clicked but it does not seem informative in terms of it's
function.

Fixes: #966.
2020-05-19 11:02:39 -04:00
Anders Kaseorg
d661895545 Remove the insecure ignoreCerts option.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-13 04:06:50 -07:00
Akash Nimare
ca9ab6168e Update support email. 2020-05-13 12:19:45 +05:30
Anders Kaseorg
bbdf2c6017 mailmap: Add Git .mailmap for zulipchat.com → zulip.com.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-12 22:57:22 -07:00
Manav Mehta
109a9bbd10 document: Remove inessential comment inherited from #902 and #909
The comment was created to document a piece of code in #902 but #909 addressed code quality
The comment is no longer essential and hence ought to be removed
2020-05-06 17:01:47 -07:00
Anders Kaseorg
22d7ef5615 main: Turn electron-connect off by default.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-06 16:58:25 -07:00
Anders Kaseorg
e03de26137 release: New release v5.2.0.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-05 12:54:35 -07:00
Anders Kaseorg
983254c310 CVE-2020-12637: Do not ignore certificate errors without ignoreCerts.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-05 12:54:35 -07:00
Anders Kaseorg
b6059077d8 Generalize permission-response mechanism to renderer-callback.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-05 12:54:35 -07:00
Anders Kaseorg
cafff9a008 new-server-form: Add missing string conversion for error message.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-05 12:54:35 -07:00
Anders Kaseorg
190204b2e5 notarize: Fix lint errors.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-05 12:54:09 -07:00
Akash Nimare
4c25c99abc notarization: Pass team short name info in code-signing. 2020-05-06 01:13:29 +05:30
Anders Kaseorg
55be93b906 dependencies: Upgrade dependencies.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-04 01:56:35 -07:00
Anders Kaseorg
34e2b3a3d0 typescript: Eradicate most any annotations.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-04 01:08:05 -07:00
Anders Kaseorg
e5ece8db9e package.json: Fix ‘npm start’ to do something useful.
The old run-dev script raced electron and tsc against each other in
parallel, which usually meant electron would run stale JS files.  We
should overhaul the build process at some point, but whatever we do,
this script isn’t going to be part of it.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-03 20:35:21 -07:00
Anders Kaseorg
40b26dbb0e dnd-util: Refine typing.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-03 20:03:41 -07:00
Anders Kaseorg
ae4f03f4ba logger-util: Clean up typing disaster zone.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-03 20:03:41 -07:00
Anders Kaseorg
8ea32a7a96 feedback: Stub send-feedback types.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-03 19:33:52 -07:00
Anders Kaseorg
6b7cce0366 ServerManagerView: Convert loading from object to Set.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-03 19:16:05 -07:00
Anders Kaseorg
73fec72e6d registerIpcs: Convert webviewListeners to array with functions.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-03 19:09:22 -07:00
Anders Kaseorg
920adfb169 CI: Clean unused steps from CI configuration.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-03 15:02:58 -07:00
Manav Mehta
98174fdcaf help: Update Help Center URL.
Earlier the menu option used to redirect to *.zulipchat.com/help which is already reachable from the webapp.
This will enable the desktop app to redirect only to zulipchat.com/help instead of the help page of the corresponding organization.

Fixes: #948.
2020-05-02 20:17:25 +05:30
Anders Kaseorg
a0c033431e electron-bridge: Add decrypt_clipboard helper.
This one helper allows us to implement browser-based social login
entirely on the server side.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2020-05-01 15:00:02 -07:00
Abhigyan Khaund
82421d843a downloadFiles: Fix issue of showing two Save As dialog box.
Currently, there are two dialog boxes shown while downloading files (in Ubuntu). One by default behavior of electron and other by the dialog box for save as feature.
This PR fixes this issue by using electron's save as dialog box.

Fixes: #947.
2020-05-01 17:55:46 +05:30
Anders Kaseorg
d9afee3330 ServerManagerView: Convert functionalTabs from object to Map.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-30 13:48:18 -07:00
Anders Kaseorg
a46f2ed618 xo: Enable import/no-mutable-exports.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-30 13:48:18 -07:00
Anders Kaseorg
9f3b4ff408 xo: Remove unicorn/string-content exclusion.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-30 13:48:18 -07:00
Anders Kaseorg
fb800f7862 xo: Enable @typescript-eslint/no-unused-vars.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-30 13:48:18 -07:00
Anders Kaseorg
ba191c3699 xo: Enable object-curly-spacing.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-30 13:48:18 -07:00
Anders Kaseorg
e49a880ed6 xo: Enable capitalized-comments.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-30 13:48:18 -07:00
Anders Kaseorg
4bfa7c9265 xo: Enable no-else-return.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-30 13:48:18 -07:00
Anders Kaseorg
39c6fa4ace xo: Enable @typescript-eslint/member-ordering.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-30 13:48:18 -07:00
Anders Kaseorg
963c2e5388 xo: Enable @typescript-eslint/restrict-plus-operands.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-30 13:48:18 -07:00
Anders Kaseorg
849df4adaf xo: Enable @typescript-eslint/restrict-template-expressions.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-30 13:48:18 -07:00
Anders Kaseorg
fc6ff83485 xo: Enable no-alert.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-30 13:48:18 -07:00
Anders Kaseorg
5ae2a717fa xo: Enable padding-line-between-statements.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-30 13:48:18 -07:00
Anders Kaseorg
cfdc08a038 xo: Enable unicorn/catch-error-name.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-30 13:48:18 -07:00
Anders Kaseorg
b76467529d xo: Upgrade xo to 0.30.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-30 13:48:18 -07:00
Anders Kaseorg
bb88a7b7a8 xo: Handle floating promises.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-30 13:48:18 -07:00
Anders Kaseorg
0225778050 typescript: Annotate queueDomain.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-30 13:48:18 -07:00
Anders Kaseorg
2154b191c8 typescript: Annotate url in WebviewProps.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-30 13:48:18 -07:00
Anders Kaseorg
4093304b4d typescript: Annotate electron-updater events.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-30 13:48:18 -07:00
Anders Kaseorg
2e03f779e8 typescript: Annotate setMenu props.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-30 13:48:18 -07:00
Manav Mehta
9464390070 docs: Update changelog for release 5.1.0. 2020-04-30 00:48:31 +05:30
Manav Mehta
16f0af8853 Remove storing of User-Agent on disk.
Fixes: #921.

Co-authored-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-26 13:30:39 -07:00
am2505
bb6d90671f typescript: Complete most TypeScript todos. 2020-04-25 15:49:10 -07:00
Anders Kaseorg
e536a03fab dependencies: Fix package-lock.json stability.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-25 13:07:28 -07:00
Anders Kaseorg
ee60702276 release: Update package-lock.json for new release v5.1.0.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-25 13:07:24 -07:00
Akash Nimare
e721211619 release: New release v5.1.0 2020-04-25 13:53:44 +05:30
Anders Kaseorg
ff671b53ef dependencies: Upgrade dependencies.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-24 22:15:20 -07:00
Akash Nimare
340eb8da99 server-icon: Handle server icon update gracefully.
Don't trigger user-facing network error for updating the server icon.
2020-04-24 11:48:27 +05:30
Anders Kaseorg
ae689ad6bb general-section: Import supported-locales.json as a JSON module.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 13:06:00 -07:00
Anders Kaseorg
7ffddded5d Configure language by value, not by index.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 13:05:59 -07:00
Anders Kaseorg
714cd926ae generateSelectTemplate: Add missing HTML escaping.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 13:05:15 -07:00
Manav Mehta
b2f4af0f49 Add feature to set application language.
User can now select application language without changing the language in the operating system.

Fixes: #855.
2020-04-22 18:08:49 +05:30
Aditya Mudgil
d7136aef25 macOS: Update dock icon.
* Added inner padding to .icns dock icon for mac
* Change inner padding of the image to 20px for each side

Fixes: #787.
2020-04-14 20:20:48 +05:30
Anders Kaseorg
73f8b21a9f scripts: Synchronize Debian scripts with electron-builder 22.4.1.
Fixes #912.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-08 14:15:43 -07:00
Akash Nimare
4ce08cb5a2 dock: Do not toggle window on clicking dock.
We are reverting back our decision after getting a lot of feedback
on this behaviour. Fromm now on, when you click on the dock icon we'll
show the app window and won't hide it after clicking again on dock.

Fixes: #914.
2020-04-07 20:16:50 +05:30
Anders Kaseorg
814ce071a3 release: New release v5.0.0.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-30 19:38:33 -07:00
Anders Kaseorg
92fb176f67 Revert "auth: Move social login process to browser."
This reverts commit 49b29bfed6 (#863).

The design of this feature is still under discussion; we expect it to
return after the security release.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-30 19:33:24 -07:00
Anders Kaseorg
a03f569af9 CVE-2020-10857: Whitelist safe URL protocols for shell.openExternal.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-30 19:33:24 -07:00
Anders Kaseorg
af59bb7c99 handleExternalLink: Do not navigate the current window.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-30 19:33:24 -07:00
Anders Kaseorg
4390966a62 Always show downloaded files in file manager.
shell.openItem is unsafe.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-30 19:33:24 -07:00
Anders Kaseorg
a6d942fe6c CVE-2020-10858: Lock down session permission requests.
This fixes a vulnerability reported by Matt Austin.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-30 19:33:24 -07:00
Anders Kaseorg
9d4093b3d8 CVE-2020-10856: Enable context isolation.
This fixes a vulnerability reported by Matt Austin.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-30 19:33:24 -07:00
Anders Kaseorg
20a6c5d128 preload: Use IPC for logout, shortcut, showNotificationSettings.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-30 19:33:24 -07:00
Anders Kaseorg
c843e179fc tray: Remove tray variable from window.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-30 19:33:24 -07:00
Anders Kaseorg
438d4fffa7 notification: Convert loadBots from jQuery to fetch.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-30 19:32:23 -07:00
Tim Abbott
5c164bfa7d webview: Disable insecure content.
Zulip servers in production are designed to only serve content over
HTTPS.  And a development environment's root page will be served over
HTTP.

So there is no purpose in enabling allowInsecureContent, even
conditionally for use against Zulip development environments; we should
just remove the setting.
2020-03-30 19:32:23 -07:00
Anders Kaseorg
cbc89a72a2 tray: Work around Electron segfault on certain platforms.
Set the tray icon’s context menu immediately after creating the Tray
object.  This seems to prevent an Electron segfault at startup on
certain platforms, such as Ubuntu 16.04 i386.  See
https://github.com/electron/electron/issues/22652 and its linked
issues.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-30 19:26:29 -07:00
Anders Kaseorg
fb1e163130 typescript: Fix errors hidden by skipLibCheck.
This requires temporarily downgrading to @types/node@^12 (see
https://github.com/electron/electron/issues/21612).

Leave skipLibCheck on for now as it still saves a few seconds when
running tsc.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-26 16:35:13 -07:00
Anders Kaseorg
2ebeeedba8 dependencies: Move fs-extra from devDependencies to dependencies.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-24 21:07:25 -07:00
Anders Kaseorg
82a7f97ca6 dependencies: Upgrade dependencies.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-23 17:22:48 -07:00
Anders Kaseorg
55eb768064 xo: Upgrade xo to 0.28.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-23 16:53:14 -07:00
Anders Kaseorg
611932c66d xo: Unabbreviate variable names.
To satisfy unicorn/prevent-abbreviations in xo 0.28.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-23 16:53:12 -07:00
Anders Kaseorg
5deffa5022 changelog: Add missing changelog entry for v4.0.3.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-23 15:08:21 -07:00
Anders Kaseorg
6f01f1362a js: Declare 'use strict' on tests too.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-09 22:14:23 -07:00
Anders Kaseorg
9d2739f050 js: Declare 'use strict' on all scripts and no modules.
And enable the import/unambiguous ESLint rule as a check on our
partition between scripts and modules.  After this commit, if you add
a new file and get this error:

  ✖  1:1  This module could be parsed as a valid script.  import/unambiguous

* For a module, add an `import` or `export` declaration to make the
  file unambiguously a module (the empty `export {};` declaration
  suffices).
* For a script, add the file to the xo overrides section of
  package.json that marks it "sourceType": "script", and add a 'use
  strict' declaration.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-09 20:04:43 -07:00
Akash Nimare
01f6e77237 macOS: Fix undo redo not working on macOS.
The default API provided by Electron doesn't work
as expected. More info here -
https://github.com/electron/electron/issues/15728

Fixes: #866.
2020-03-10 00:32:05 +05:30
Manav Mehta
7ac35cc087 macOS: Replace deprecated isDarkMode() with shouldUseDarkColors.
Fixes: #891.
2020-03-08 14:55:43 +05:30
Anders Kaseorg
32e6b3054f xo: Use more xo defaults.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-04 21:46:18 -08:00
Anders Kaseorg
40bf2a1f20 xo: Lint *.js too.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-04 21:22:04 -08:00
Anders Kaseorg
dee2f05ac0 locale-helper: Move supported-locales.js to supported-locales.json.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-04 20:45:57 -08:00
Anders Kaseorg
ca5de73155 xo: Reenable several easy rules.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-04 20:15:01 -08:00
Anders Kaseorg
d0f8c040c7 package.json: Add tsc to test script.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-04 18:26:06 -08:00
Anders Kaseorg
7cf40f1e08 typescript: One more switch to ES export syntax.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-04 18:15:42 -08:00
Anders Kaseorg
598c0df60b package.json: Reformat to match npm generated output.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-04 17:52:10 -08:00
Anders Kaseorg
d3bcd7306a typescript: Switch to ES import/export syntax.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-04 17:21:03 -08:00
Anders Kaseorg
b3261bcdff js: Explode more singleton classes to modules.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-04 16:27:44 -08:00
Manav Mehta
20c6f487c4 typescript: Implement some TODOs. 2020-03-04 14:21:25 -08:00
Anders Kaseorg
340797ca10 typescript: Refine some type annotations to avoid any.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-04 12:12:31 -08:00
Anders Kaseorg
220aac2d54 js: Explode singleton classes to modules.
Singleton classes may have a purpose.  This was not that purpose.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-04 11:54:45 -08:00
Akash Nimare
d9f6cf4cc9 docs: Update recommended node version for development. 2020-03-04 16:59:21 +05:30
Anders Kaseorg
dc3e5d4930 package.json: Sort xo overrides.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-03 21:50:02 -08:00
Anders Kaseorg
fc2b80c36a main: Fix realm icon updating.
Commit c937317ecf (#605) should have
updated this, but didn’t.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-03 21:50:02 -08:00
Anders Kaseorg
ab667d8053 checkCertError: Fix showMessageBox usage.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-03 21:50:02 -08:00
Manav Mehta
8b9a10a23d Update report issue placeholder.
Fixes: #873.
2020-03-04 11:16:31 +05:30
Anders Kaseorg
15af3e732f sentry-util: Hard-code the Sentry DSN.
Commit 088ddf9c62 (#755) does not work,
because neither the .env file nor the environment variables it
provides are available to normal users at runtime.  This silently
broke Sentry data collection.  When we upgraded @sentry/electron in
commit 107e522914, the silent failure
became an error that prevented the app from starting.

The Sentry DSN is not a secret, so we should just commit it to the
repository.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-04 11:07:00 +05:30
am2505
534f4c1463 Convert Promise to async-await.
Fixes #878.
2020-03-03 20:40:10 -08:00
Anders Kaseorg
063324550e run-dev: Fix for one package.json structure.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-03 20:00:36 -08:00
Anders Kaseorg
268471df6b typings: Remove redundant ZulipWebWindow.$.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-03 18:17:13 -08:00
Anders Kaseorg
ca6b2312be typings: Get Window.Notification from upstream.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-03 18:16:01 -08:00
Anders Kaseorg
ff026e5763 typings: Get NotificationOptions.silent from upstream.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-03 18:14:13 -08:00
Anders Kaseorg
8f810481e3 typings: Get requestIdleCallback from DefinitelyTyped.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-03 18:13:26 -08:00
Anders Kaseorg
f91e95647a typings: Use type declarations from DefinitelyTyped.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-03 18:01:24 -08:00
Anders Kaseorg
e9536f247b dependencies: Use one package.json structure.
The two package.json structure is no longer needed.
https://www.electron.build/tutorials/two-package-structure

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-03 11:02:59 -08:00
Anders Kaseorg
067cbf32a1 build: Fix cld sources exclusion.
As of commit 107e522914, @paulcbetts/cld
was renamed to cld.

docs is not being included anyway since it’s outside of app.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-03 10:59:41 -08:00
Anders Kaseorg
6824978114 dependencies: Remove @types/dotenv compatibility stub.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-02 20:31:20 -08:00
Anders Kaseorg
fa86f1ca25 dependencies: Upgrade everything to latest.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-02 20:31:19 -08:00
Anders Kaseorg
a63b3873ae dependencies: Remove electron-debug.
We already implement all of its functionality.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-02 20:15:43 -08:00
Anders Kaseorg
5064ea4b47 dependencies: Upgrade node-json-db from 0.9.2 to 1.0.3.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-02 19:52:39 -08:00
Anders Kaseorg
6b0d8520c5 dependencies: Remove unused dependencies assert, cp-file, is-ci.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-02 19:19:37 -08:00
Anders Kaseorg
4b16164155 cleanup: Remove unused tests/e2e directory.
It is not used even by test-e2e.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-02 19:18:25 -08:00
Anders Kaseorg
6036a44fb2 new-server-form: Remove useless .server-save-action wrappers.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-02 19:08:45 -08:00
Anders Kaseorg
598b96b6e8 webview: Wait for dom-ready before sending messages.
Fixes tests/test-add-organization.js.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-02 18:52:37 -08:00
Anders Kaseorg
675bc2f06c appveyor.yml, .travis.yml: Test current Node.js releases.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-03-02 16:30:12 -08:00
Tim Abbott
eb2988a5e4 dependencies: Update typescript and typescript-eslint.
The changes are mostly done via `xo --fix`; the other changes are
either trivial or disabling new linter rules that we plan to address
in future commits.
2020-02-29 23:39:55 -08:00
Tim Abbott
39ea18228c dependencies: Update gulp testing packages. 2020-02-29 22:54:50 -08:00
Tim Abbott
909e0f07e3 dependencies: Upgrade linters and fix linter errors.
The changes here are mostly straightforward; the one exception is
removing a zulipdev.org hack.

We disable some lint rules we'll want to address later (E.g. we want
to switch to using async/await rather than .then()).  But those are
out of scope for this commit.
2020-02-29 22:47:42 -08:00
Tim Abbott
31af6596bf dependencies: Upgrade Electron to version 8.
This is the latest Electron release, which means we're now getting
nearly modern Chrome (hopefully with fewer rendering bugs and better
performance).
2020-02-29 21:49:19 -08:00
Tim Abbott
3c9914542f badge: Clear badge counts on Linux as well.
My Linux desktop environment doesn't display unread badges, it seems,
but this is clearly how this code should read.
2020-02-29 21:39:56 -08:00
Tim Abbott
f4b9605742 electron: Update some setter/getters to user newer properties.
This removes a few deprecation warnings on app startup.
2020-02-29 21:39:56 -08:00
Tim Abbott
e2fc9241fa dependencies: Upgrade to Electron 7.
This works without any other changes, thanks to Electron's deprecation
process being done over multiple releases.
2020-02-29 21:39:56 -08:00
Tim Abbott
3b18357c74 download: Use removeListener for removing the updated listener.
This is slightly cleaner code, and also fixes a typescript error (that
might be a bug) we'll get when we upgrade to Electron 7.
2020-02-29 21:39:56 -08:00
Tim Abbott
c4beedf740 proxy: Migration to use async/await.
This is required for the upgrade to Electron 7, which removes the old
callback-based form of these APIs.
2020-02-29 21:39:56 -08:00
Anders Kaseorg
b34bf7236f .travis.yml: Fix npm ci invocation for app directory.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-29 18:49:01 -08:00
Tim Abbott
e32968b2f3 preferences: Convert one more dialog to use async/await.
This should have been in the main version update commit.
2020-02-29 18:32:09 -08:00
Anders Kaseorg
747fbb5ab0 .travis.yml: Run npm ci, not npm install.
This enforces that package-lock.json is up to date in Git.

Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-29 18:30:05 -08:00
vsvipul
107e522914 deps: Update Electron and related packages to Electron v6.
This updates most of our direct dependencies to much newer versions
(Electron v6, with compatible versions of related packages like
Spectron).

Further, it updates all of our recursive dependencies with `npm update
--depth=999`.

Modified by tabbott to migrate to async/await for dialogs rather than
the old synchronous API.
2020-02-29 18:28:42 -08:00
Tim Abbott
c83bc08359 i18n: Add additional automatically output strings.
I'm skeptical of the setup that these are generated dynamically while
working in the development environment; that seems not robust.
2020-02-29 17:38:23 -08:00
Anders Kaseorg
ef0b056437 package.json: Fix clean-ts-files script to spare app/node_modules.
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-02-29 17:03:53 -08:00
Tim Abbott
9370f783ba i18n: Update generated translation data.
I feel like this should probably not be in Git.
2020-02-29 16:14:00 -08:00
Tim Abbott
227e59fee2 gitattributes: Disable binary handling of lock files.
This makes it possible to see what packages change after npm
operations.
2020-02-29 13:09:29 -08:00
Akash Nimare
b7147d0b29 webview: Update web security preference.
Electron docs suggests that we should not use
`disablewebsecurity` thus removing the same.
2020-02-27 16:55:47 +05:30
Akash Nimare
f93053eb20 release: New release 4.1.0-beta. 2020-02-27 10:37:55 +05:30
ViPuL
49b29bfed6 auth: Move social login process to browser.
Moves the social login to browser since there
was no way to verify the authencity of the
auth process for a custom server and to
prevent phishing attacks.

Fixes #849.

Co-authored-by: Kanishk Kakar <kanishk.kakar@gmail.com>
2020-02-25 20:05:27 +05:30
Akash Nimare
0fb610f858 macOS: Add colorless tray icon for macOS.
Fixes: #825.
2020-02-05 21:41:10 +05:30
ViPuL
1d9a923245 startup: Use inbuilt electron API for autostartup. (#859)
Switch from using the external auto-launch module to
inbuilt setLoginItemSettings for windows and macOS,
as some users reported issues on windows.

Fixes: #851.
2020-01-28 12:26:41 +05:30
Ross Brunton
9582d32de8 Added option to select download locations.
Added an option that, when enabled, will mean any file downloads that
would normally go to ~/Downloads (or wherever), in fact prompt.
2020-01-21 16:41:56 +05:30
ViPuL
a2a21631f2 Decode server name in Window menu. 2020-01-08 11:58:44 +05:30
Akash Nimare
9490265a03 dock: Toggle app on clicking the dock icon. 2019-12-29 00:49:46 +05:30
Brandon Liu
70bd619fa8 Fix size of macOS dock icon.
Fixes: #845.
2019-11-30 17:07:31 +05:30
Akash Nimare
c5797e4edb release: New release v4.0.3. 2019-11-21 12:47:29 +05:30
Kanishk Kakar
32321daef2 docs: Add release notes for v4.0.2-beta. (#841) 2019-11-17 04:27:55 +05:30
Akash Nimare
95a9568ece beta-release: New release v4.0.2-beta. 2019-11-13 14:56:34 +05:30
Kanishk Kakar
e7a885a1fb macos: Enable notarization for macOS Catalina.
This fixes the issue for Catalina users.
2019-11-13 14:55:11 +05:30
Akash Nimare
17d4d97e2e certificate: Make certificate location dynamic.
* certificate: Make certificate location dynamic.

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

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

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

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

Fixes: #641.
2019-08-15 00:35:00 +05:30
Priyank Patel
ab62b8b5bb Remove accidently commited translation-util.js file.
This file was accidently committed in 77a1fc0bd3
when migrating old PR to use typescript. This commit removes it.
2019-08-14 12:50:47 -04:00
Kanishk Kakar
ce071dcac9 docs: Add release notes for v4.0.0. 2019-08-11 01:13:37 +05:30
Akash Nimare
04c1109d43 release: New release v4.0.0. 2019-08-07 22:17:34 +05:30
Prakhar Uniyal
26f321e7f9 preference: Fix visually broken delete custom css button.
There were couple of issue solved by this commit:
  - The button had extra 10px margin to right; this comes from .action class
  - The "Delete" text and icon were not aligned
  - The button looks bigger than other buttons

Both of which made the button look broken. We fix first by changing
.css-delete-action to #css-delete-action in css file; git grep shows
this is only used once and is used as id not a class. Then we use
display flex to fix the alinging of icon and text. We add box-sizing border-box
which solves the bigger width issue. We remove extra 10px margin that comes from
.action.

Fixes: #801.
2019-08-07 20:57:44 +05:30
Kanishk Kakar
47b729cbc2 i18n: Use English as fallback language.
In case app.getLocale() returns a falsey value, English is used as the
app language.
2019-08-02 00:45:12 +05:30
Kanishk Kakar
6d34da5953 i18n: Add translations to the new server form. 2019-08-02 00:45:12 +05:30
Kanishk Kakar
cfd1ebc643 i18n: Add translations to settings sidebar. 2019-08-02 00:45:12 +05:30
Kanishk Kakar
dab30dfb7a i18n: Add translations for shortcuts pane. 2019-08-02 00:45:12 +05:30
Kanishk Kakar
e2bb28c738 i18n: Add translations for connected orgs. 2019-08-02 00:45:12 +05:30
Kanishk Kakar
ebd4d9a69b i18n: Add translations for network settings. 2019-08-02 00:45:12 +05:30
Kanishk Kakar
ab80315846 i18n: Add translations for general settings. 2019-08-02 00:45:12 +05:30
Kanishk Kakar
c78ea51ec9 i18n: Change menu according to system locale. 2019-08-02 00:45:12 +05:30
Kanishk Kakar
d521f533f0 i18n: Add new values to translations 2019-08-02 00:45:12 +05:30
Kanishk Kakar
67a69f0dc2 i18n: Add translations to app menu
* Uses new keys added to locale-template.json
* Disregards org names
2019-08-02 00:45:12 +05:30
Kanishk Kakar
d99cc0d49c locales: Change translate API to handle rate limit
Also add catch block to log possible errors.
2019-08-02 00:45:12 +05:30
Kanishk Kakar
77a1fc0bd3 i18n: Setup module and add translation-util.js
* Add i18n package.
* Use system locale for TranslationUtil.
2019-08-02 00:45:12 +05:30
Akash Nimare
cf96e94470 zoom: Fix zoom-in shortcut symbol.
Use symbol instead of symbol name in accelerators in menu items.

This is a hackish solution for the following upstream issue -
https://github.com/electron/electron/issues/10371

Fixes: #718.
2019-07-31 18:37:03 +05:30
Akash Nimare
4395edc3de macos-shortcut: Add meta key for cmd. 2019-07-31 18:37:03 +05:30
Akash Nimare
66479e0d88 docs: Update auto-update docs for preset orgs. 2019-07-31 04:43:58 +05:30
Kanishk Kakar
7c44ef5e41 enterprise: Restrict EnterpriseUtil method calls.
* Add a boolean flag to EnterpriseUtil so unnecessary function calls
don't take place for non-admin users.
2019-07-31 04:32:10 +05:30
Kanishk Kakar
296c83949f typescript: Define DialogBoxError in messages.ts. 2019-07-31 04:32:10 +05:30
Kanishk Kakar
ac6f14f5b4 settings: Show tooltip when setting disabled.
If setting has been locked by an admin, then a tooltip is shown in the
settings page.
2019-07-31 04:32:10 +05:30
Kanishk Kakar
20ac6bf921 enterprise: Silently fail config updates.
If a config item exists in enterprise config and we're not trying to
override, then setConfigItem returns.
2019-07-31 04:32:10 +05:30
Kanishk Kakar
5e9f9aa9a2 enterprise: Disable autoUpdate on admin setting. 2019-07-31 04:32:10 +05:30
Kanishk Kakar
4efa98118b enterprise: Init enterpriseSettings from file.
Read enterprise settings while loading the app and give preference to
global config.
2019-07-31 04:32:10 +05:30
Kanishk Kakar
a7887211ac enterprise: Raise error when removing orgs.
When a user tries to remove a preset org, we raise an error asking them
to contact their sys admin.
2019-07-31 04:32:10 +05:30
Kanishk Kakar
33fadcd876 enterprise: Stop loading gif if no domains added.
If no domains have been added, stop the loading gif and open the new
server form.
2019-07-31 04:32:10 +05:30
Kanishk Kakar
e7e92ebecc enterprise: Show error when all preset orgs fail.
On app load, if all the preset orgs fail to get added to the server,
then we show an errorBox.
2019-07-31 04:32:10 +05:30
Kanishk Kakar
6c29af6766 docs: Add enterprise docs for preset orgs. 2019-07-31 04:32:10 +05:30
Kanishk Kakar
eabac3ae81 enterprise: Show loading gif view.
When no other orgs have been added and presetOrgs have been specified in
enterprise config, loading gif will appear.
2019-07-31 04:32:10 +05:30
Kanishk Kakar
fc0385433e enterprise: Detect changes in orgs config.
Allow app to identify if presetOrgs have changed in enterprise config
and change domains based on user response.
2019-07-31 04:32:10 +05:30
Kanishk Kakar
400c02f264 enterprise: Add support for preset orgs.
Enable configuration of preset organizations in the enterprise config
file and load them when app loads.
Partially valid array of URLs accepted while loading app.
2019-07-31 04:32:10 +05:30
Kanishk Kakar
be841cff34 enterpise: Add enterprise-util for custom configs. 2019-07-31 04:32:10 +05:30
Kanishk Kakar
7d426c4791 sidebar: Revert to fallback icon only when needed.
Fix an issue where server icon was replaced with character icon on
network issues.
2019-07-30 02:14:40 +05:30
Akash Nimare
939aa50e9b menu: Disable window sub-menu.
Disable switch to next-org and previous org when the
server length is less than one.

Fixes: #799.
2019-07-26 05:14:45 +05:30
Akash Nimare
dc9b9a1dc3 zoom-in: Fix zoom-in shortcut. 2019-07-24 17:28:59 +05:30
Kanishk Kakar
b0da718bfc sidebar: Sync loading indicator with loading GIF.
* Disable loading indicator (same as going back in settings)
* Show loading indicator on reload
2019-07-24 02:34:05 +05:30
Priyank Patel
7ea6255e65 darwin-notifications: Use electron_bridge notification reply api if supported.
To avoid using the brittle code we have for inline notification reply
currently, we added more future proof in webapp via the electron_bridge,
we use the newer API if it is supported.

Fixes: #794.
2019-07-23 22:08:24 +05:30
Kanishk Kakar
7338f802e1 docs: Add release notes for v3.1.0-beta. 2019-07-22 20:09:04 +05:30
Akash Nimare
6860f6d836 release: New release v3.1.0-beta. 2019-07-19 00:37:12 +05:30
Kanishk Kakar
088ddf9c62 dev: Use .env file for Sentry DSN.
Reads .env file in root folder of repo to get Sentry DSN for builds.
2019-07-19 00:23:10 +05:30
Priyank Patel
95da6c0d58 dependencies: Remove @types/node.
Electron already depends on the @types/node and extends
it in electron.d.ts.
2019-07-17 00:22:17 +05:30
Akash Nimare
1a3d3fc5ff system-util: Fix user-agent. 2019-07-17 00:22:17 +05:30
Priyank Patel
535039216b package.json: Add clean-ts-files script. 2019-07-17 00:22:17 +05:30
Priyank Patel
f449db1dc1 package.json: Run tsc before running electron-builder. 2019-07-17 00:22:17 +05:30
vsvipul
73e0e81abd gulp: Add tasks for compile and run from src. 2019-07-17 00:22:17 +05:30
Priyank Patel
dd4f3047c3 tools: Add tools for running typescript compiler and electron app.
This tool handles edge cases where all the typescript files might not
be compiled when the electron process starts this defer that; it runs
tsc compiler first.

Next, we want to only run the compiler if the files are not
compiled.
2019-07-17 00:22:17 +05:30
vsvipul
e4ef0e195a typescript: Do final cleanup of issues.
Using `import * as` import syntax causes some problem if the
module exports a class or function. Because the whole point of
star import is to import every property the module exports. It turns
out we have been using it incorrectly in many places which this commit
fixes.

Then we fix a linting error by adding a eslint disable rule to solve
it along with a TODO because the way we currently do it is wrong.

Finally, to conclude this cleanup, we merge all the .gitignore paths
into once now that we can.
2019-07-17 00:22:17 +05:30
Priyank Patel
00a925e39e typscript: Fix electron-debug not found error in app.
The import in question is electron-is-dev one, others we are
just fixing while we are at it. The problem with this way of importing
`import * as` is that the isDev would be { default: true | false } not
true | false as we like it to be. This causes and error in production app
because electron-debug isn't installed.

Next, we fix the electron-connect import in main.js.
2019-07-17 00:22:17 +05:30
Priyank Patel
c34b4f9151 tsconfig: Remove declare module * declaration and normalize imports.
The change in this commits are pretty involved but cannot be split
into small commits. The main changes in this commits are:
    * Remove declare module * now that we don't need it
    * Normalize import paths so typescript is happy

Previously, we were using wrong import paths and so typescript couldn't
really provide full types information for imports. The wrong paths isn't
a bug because it was done to make sure it work when it was imported via a
script tag; we fix this by using require inside the script tag in main.html.

Also, did audit to make sure we correctly use __dirname not that it's
value will be diffrent, it won't be js/ but will be respective to the file
path of the module.
2019-07-17 00:22:17 +05:30
Priyank Patel
8a13f7914c typescript: Fix incorrect types for feedback.ts.
Not that the previous types were incorrect but they were wrong.
SendFeedback extends the HTMLElement and is a class so it has it's
own properties so extended the properties accordingly.
2019-07-17 00:22:17 +05:30
vsvipul
b13c86e2db typescript: Migrate main.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
4fb4773a2b typescript: Migrate tray.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
deda98b9d4 typescript: Migrate preload.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
f98457a0cb typescript: Add typings for window and page_params. 2019-07-17 00:22:17 +05:30
vsvipul
3436fb3e68 domain-util: Change object to any in domain-util. 2019-07-17 00:22:17 +05:30
vsvipul
448822a5fb typescript: Migrate messages.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
6e07945d7c typescript: Migrate spellchecker.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
54a8e33b01 typescript: Migrate feedback.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
93c22ca89d gitignore: Add js and resources dir to gitignore. 2019-07-17 00:22:17 +05:30
vsvipul
a2fca89551 typescript: Migrate electron-bridge.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
1516452a8c tsconfig: Turn off noUnusedParamaters rule.
We want to turn off this rule because this requires us to add _
in front of parameter that are unused which we have lot of, for example,
the ipc listeners.
2019-07-17 00:22:17 +05:30
vsvipul
700e73105e typescript: Migrate network.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
c41bbf9dfd typescript: Migrate shortcuts-section.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
347de5981a typescript: Migrate servers-section.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
d932a2b2c4 typescript: Migrate server-info-form.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
34e403d0d2 typescript: Migrate preference.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
77c5b8af30 typescript: Migrate new-server-form.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
b2bc268c09 typescript: Migrate network-section.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
8c52b563bf typescript: Migrate nav.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
a8109c5106 typescript: Migrate general-section.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
76818b7def typescript: Migrate find-accounts.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
a5ea3d475f typescript: Migrate connected-org-section.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
2f9205b0c4 typescript: Migrate base-section.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
3f00beb291 typescript: Migrate badge-settings.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
72f356de9c typescript: Migrate add-certificate.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
482264d878 gitignore: Add pages/ dir to gitignore. 2019-07-17 00:22:17 +05:30
Priyank Patel
d7725c121c typescript: Migrate default-notification to typescript. 2019-07-17 00:22:17 +05:30
Priyank Patel
2b19cdecf2 typescript: Migrate index.js to typescript. 2019-07-17 00:22:17 +05:30
Priyank Patel
1182af23e4 typescript: Migrate darwin-notification to typescript. 2019-07-17 00:22:17 +05:30
Priyank Patel
6192801083 typescript: Migrate helper.js to typescript. 2019-07-17 00:22:17 +05:30
Priyank Patel
63a913487c .gitignore: Ignore typescript compiled files in notifications directory. 2019-07-17 00:22:17 +05:30
vsvipul
77899f9f48 typescript: Migrate webview.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
2b1e594286 tabs: Fix constructors in webview tabs.
template() property used earlier in tab.ts was a
property of its child classes, which causes problems
in compiler, which this PR fixes.
2019-07-17 00:22:17 +05:30
vsvipul
312dc1daaf typescript: Migrate functional-tab.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
744d056e26 typescript: Migrate server-tab.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
433f11ebd1 typescript: Migrate tab.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
506982b4c6 typescript: Migrate handle-external-link.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
4adda8b8ae typescript: Migrate base.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
fbef3e4d7b utils: Change export statement in domain util. 2019-07-17 00:22:17 +05:30
vsvipul
f076343068 gitignore: Ignore typescript compiled file in js/components. 2019-07-17 00:22:17 +05:30
Priyank Patel
d5c9e638ca typescript: Migrate preventdrag to typescript. 2019-07-17 00:22:17 +05:30
Priyank Patel
d5a343d549 typescript: Add needed type annotations after rebasing to master. 2019-07-17 00:22:17 +05:30
Priyank Patel
4412f60435 typescript: Add types to node-json-db variables.
This adds JsonDB type to db variables we use, which were
previously any due to issue with node-json-db not specifying
the types field in package.json.
2019-07-17 00:22:17 +05:30
Priyank Patel
cec2b3136d dependencies: Upgrade node-json-db to v0.9.2.
This version was released just for the fact that the
bundles typescript declaration file was not defined
in package.json hence typescript couldn't detect it.
2019-07-17 00:22:17 +05:30
Priyank Patel
9ba2e0ad1e .editconfig: Set the indent_size to 4.
This will make GitHub show 4 spaces for a tab instead of 8; makes
PR diffs more readable.
2019-07-17 00:22:17 +05:30
vsvipul
eb30f48a82 typescript: Migrate index.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
edc5b92ab4 typescript: Migrate linuxupdater to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
29753b1a46 typescript: Migrate autoupdater to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
f490efb2df .gitignore: Ignore .js files in app dir. 2019-07-17 00:22:17 +05:30
vsvipul
bb7426a789 utils: Change export statement in linux-update-util. 2019-07-17 00:22:17 +05:30
vsvipul
e7edfffa89 config: Linting changes to package.json and tsconfig.json 2019-07-17 00:22:17 +05:30
Priyank Patel
fb700350f9 typescript: Fix some issues with recent changes.
In domain-util, we were using `import * as ` syntax which
compiled down to `__importStar(require('...'))` which were
not giving the same result as before and causing errors. This
fixes that.

In logger-util, we were missing the call to .apply function
so it should have been `this._log.apply(...)`, but then when we
use apply typescript still complains about merging string with any[]
so we decided to go with bind.
2019-07-17 00:22:17 +05:30
Priyank Patel
79d0688bcd .gitignore: Ignore typescript compiled files. 2019-07-17 00:22:17 +05:30
Priyank Patel
c6c36b698d typescript: Migrate domain-util to typescript. 2019-07-17 00:22:17 +05:30
Priyank Patel
8ef39553b8 typescript: Migrate linux-update-util to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
7374fcbe78 typescript: Migrate config-util to typescript. 2019-07-17 00:22:17 +05:30
Priyank Patel
aa2c55538f typescript: Migrate params-util.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
4d679acb0c typescript: Migrate logger-util to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
795b9fdc17 xo: Upgrade xo to v0.24.0.
This version also includes new rule we want to ignore.
2019-07-17 00:22:17 +05:30
Priyank Patel
82d9f795c2 xo: Disable some of the typescript-eslint rule.
One rule requires use to move all the functions which is
unnessacary churn.
2019-07-17 00:22:17 +05:30
vsvipul
ec91d46f7b typescript: Migrate menu.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
728418742d typescript: Migrate proxy-util to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
8b0e9b55fb typescript: Migrate reconnect-util to typescript. 2019-07-17 00:22:17 +05:30
Priyank Patel
b40be8d891 xo: Disable lines-between-class-members rule.
Apparently this doesn't work well with typescript files.
For example this will throw this linting error:
class Test {
  a: boolean;
  b: string; // needs a new line before this one to fix this linting error
}
2019-07-17 00:22:17 +05:30
Priyank Patel
e12d69720d typing.d.ts: Add comment for why we use declare module. 2019-07-17 00:22:17 +05:30
vsvipul
8353c4de16 typescript: Migrate dnd-util to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
8c3a2a80c7 typescript: Migrate link-util to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
5c4221fc31 typescript: Migrate default-util to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
a57c269935 typescript: Migrate common-util to typescript. 2019-07-17 00:22:17 +05:30
Priyank Patel
ce81a1b367 typescript: Fix wrong import syntax in request-util.ts.
Using the previous syntax was wrong because the file export
the class For example we would use previous syntax if the class
was export by `module.export = { ProxyUtil }`.
2019-07-17 00:22:17 +05:30
vsvipul
26e97c8746 typescript: Migrate certificate-util to typescript. 2019-07-17 00:22:17 +05:30
Priyank Patel
393a0fc2be request-util: Fix linting error. 2019-07-17 00:22:17 +05:30
vsvipul
b0348d6bca typescript: Migrate system-util.js to typescript.
We use to check for a case where os.platform() == win64
which is not possible so removed the condition.
2019-07-17 00:22:17 +05:30
vsvipul
9c68fcdda8 typescript: Migrate request-util to typescript.
We use newer javascript method for string that linting enforces like
startsWith, includes.
2019-07-17 00:22:17 +05:30
vsvipul
543ead4da3 typescript: Migrate sentry-util.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
b5f8d54aea typescript: Migrate startup.js to typescript. 2019-07-17 00:22:17 +05:30
vsvipul
7b07a20c04 typescript: Setup typescript project.
This adds all the dependencies and tsconfig.json and setup
for working on the typescript migration.
2019-07-17 00:22:17 +05:30
Akash Nimare
6b1a55c7e4 macOS: Support dark mode on macOS.
Fixes: #792.
2019-07-16 21:14:05 +05:30
Akash Nimare
d46bdba8ce asar: Unpack node binding files.
We have noticed that on Windows, Zulip app is creating a temp file at %appdata%/Local/Temp
as <guid>.tmp.node on each run. Adding *.node files to asarUnpack fixes this issue.
More info - https://github.com/brave/browser-laptop/issues/12534

Improves: #789.
2019-07-15 18:45:27 +05:30
Tyler Thrailkill
d3bc765e59 Add missing transitive dependencies.
zulip was using a dependency on fs-extra that was resolving transitively
through electron. This adds the dependency explicitly. This is safer and
allows tools like pnpm to function.
2019-07-02 15:27:46 +05:30
Priyank Patel
613e44ef8d reconnect-util: Gracefully reload current webview. (#783)
Before we were destroying, removing the webview elements when we
need to reload webviews which cause unwanted side-effect for example
drafts not saving because no beforeunload event handler isn't run.

Fixes: #767.
2019-07-01 23:35:50 +05:30
ViPuL
f0e4a3bc53 menu: Disable menu items on non-server pages.
Disables 'Copy Zulip URL' and 'Back' and 'Forward'
when user is on non-server pages, like Settings page.
Also, simplifies the logic for setting enable state
for 'Switch to Next Organization' and 'Switch to
Previous Organization'.
2019-06-29 01:46:14 +05:30
Kanishk Kakar
427c86afaf spellchecker: Improve auto detection of spellchecker language.
*Enabling spellchecker forces app to auto-detect the language the user
is typing in.

*Provide context of the server language when initialising spellchecker.

Fixes: #542.
2019-06-25 22:29:13 +05:30
vsvipul
0e89880cfb proxy-util: Fix syntax error.
Fixes a wrongly-placed bracket.
2019-06-25 21:05:41 +05:30
vsvipul
b1f1b49982 preference: Remove unused defaultId parameter.
defaultId is not a property of OpenDialogOptions.
2019-06-25 21:05:41 +05:30
Priyank Patel
390bf907e5 components: Remove unused isLoading function from Tab. 2019-06-23 00:46:38 +05:30
Akash Nimare
02c8a27567 menu: Open help page of current active server.
* menu: Open help page of the currently active server.

Fixes: #758.
2019-06-21 01:02:48 +05:30
Kanishk Kakar
db4e8e5129 db: Fix JSON db errors on tab switch. 2019-06-20 20:54:30 +05:30
Kanishk Kakar
88b764dcc9 sidebar: Add loading indicator.
* Browser-like loading indicator added to the sidebar.
Shows when the app is loading a page. Inactive once the user starts
navigating on the webpage, and in settings.

* Add tooltip saying "Loading" to indicator.

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

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

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

Fixes #341.
2019-06-11 22:44:06 +05:30
Kanishk Kakar
139496b716 settings: Add field to find accounts by email.
Takes in an organization URL for taking the user to /accounts/find the page.
2019-06-04 21:29:33 +05:30
Akash Nimare
f6239132c1 electron: Update electron to v3.1.10. 2019-05-30 00:59:01 +05:30
Kanishk Kakar
6451373ff6 changelog: Add release notes for v3.0.0. 2019-05-22 01:20:21 +05:30
Akash Nimare
6f74fea0f3 release: New release v3.0.0. 2019-05-21 02:58:23 +05:30
Akash Nimare
405d938223 Update docs and code to point to new repo url.
This is because we have changed the repo name to zulip-desktop.
2019-05-21 02:33:13 +05:30
ViPuL
c6b7e9a9ee settings: Add option to toggle Error Reporting.
Adds an option to enable or disable sentry error
reporting under Advanced section in General Settings. 
Handles both main and renderer processes.

Also, changes the domain used to resolve proxy
in proxy-util from google.com to example.com.

Fixes #702.
2019-05-15 15:00:22 +05:30
Akash Nimare
b4073ba7ac Update package.json 2019-05-12 03:24:23 +05:30
Akash Nimare
b504f31c26 config: Remove unused icon file. 2019-05-12 03:20:22 +05:30
Akash Nimare
f71660b3f0 builder: Update electron-builder to v20.40.2. 2019-05-12 03:16:38 +05:30
Akash Nimare
5d0516887d windows: Add a config for installer name.
There is a bug with electron-builder where it doesn't name the installer properly. 
Without this config, it always names the installer with spaces in between 
i.e. "Zulip Web Setup.exe".
2019-05-12 03:14:41 +05:30
ViPuL
0dd86f0814 nsis: Allow installing app without admin previliges.
Allow the nsis installer to install the app to
AppData for a single user so that administrator
previliges are not required while installing.

Fixes #720.
2019-05-12 02:46:19 +05:30
Rhythm Sharma
eaecc92055 spellchecker: Use server language for spellchecker for all platforms.
This is an experimental fix for spellchecker. Previously, we were only using this on macOS since on other platforms, the spellcheck-module claimed to auto-detect + switch language on the fly but looks like it's not working as expected.
2019-05-10 03:43:49 +05:30
ViPuL
4f65c36a72 menu: Remove minimize and close from File menu.
Remove duplicate entries of minimize and close from
File menu as they are already present in Window menu.
2019-05-04 14:57:58 +05:30
ViPuL
2380b650c9 focus: Fix focus after clicking back button.
Addresses the issue of the webview not being in focus
after the Back button is clicked. Now, the webview is focused
explicitly by calling focus() on click.
2019-05-03 22:03:03 +05:30
ViPuL
1d713f1df2 logs: Limit the number of lines in log files.
Limits the numbers of lines in log files to 500.

Fixes a part of #727.
2019-05-03 14:57:00 +05:30
ViPuL
7fa9c291cb lint: Implement HTML Linting with htmlhint and fix indent.
* lint: Implement HTML Linting with htmlhint and fix indent.

Implements HTML linting using htmlhint and uses
indentation rules of zulip webapp - 4 spaces.
Creates a separate file .htmlhintrc for the rules, most
of which are defaults. Also, fixes indentation in html
files and adds a missing title in about.html.

* deps: Change versioning of various dependencies to exact versions.

As we prefer to use exact working version
for dependencies, changed the versions to
exact versions.

Fixes #676.
2019-04-24 00:44:01 +05:30
ViPuL
8a40e36a63 lint: Update css lint config to match zulip config.
Updated .stylelintrc to match zulip webapp's
.stylelintrc file. Also, made the required changes
to all CSS files.

Fixes remaining CSS part of #676.
2019-04-21 17:10:26 +05:30
Kanishk Kakar
3456720135 Unify case across menus and settings.
* menu: Convert to title case.

Also, change corresponding shortcuts to title case.

* settings: Convert options to sentence case.
2019-04-18 19:38:01 +05:30
Abhigyan Khaund
43840a5245 webview: Add "role" key to webview property. 2019-04-18 19:35:21 +05:30
ViPuL
d99c29301e lint: Implement CSS linting with stylelint.
* lint: Implement CSS linting with stylelint.
* lint: Format CSS files according to lintconfig.

Adds CSS linting following the rules of zulip webapp.
Some rules have been ommitted because they required
significant changes in code. Also, creates a separate
.stylelintrc for storing css config.

Fixes CSS part of #676.
2019-04-15 16:54:02 +05:30
ViPuL
6e760973ff shortcut: Allow zoom options from numpad.
Numpad keys by default aren't supported by electron
for using in menu accelarators. Uses a workaround to
make zoom options work with Numpad keys.

Fixes: #344.
2019-04-12 17:16:04 +05:30
Rhythm Sharma
88c64e9dd6 docs: improve development guide.
Update development instructions for all the systems.
2019-04-12 17:11:08 +05:30
Rhythm Sharma
e9db11c156 shortcuts-section: Fix tip's place for Windows & Linux. 2019-04-12 17:03:15 +05:30
ViPuL
8e8de212d8 menu: Reorder file menu and add option to Add Organization.
Reorder the File Menu. Also, adds a new option to
add a new organization to the file menu.
2019-04-10 22:03:43 +05:30
Puneeth Chaganti
0e0d7e7c4a webview: Work around buggy focus switching in Electron 3.0.10.
This commit works around a couple of bugs, which seem to be upstream bugs in the
current version of Electron.

First, `webContents.isFocused()` seems to be true even if we just switched to
a tab, and are trying to set it as the focused element. To work around this, we
check if the `webview` element is the same as `document.activeElement`.

Also, as per https://github.com/electron/electron/issues/15718, it looks like
`blur` needs to be called on the currently active element, before switching
focus on another element i.e., calling `focus` on it.

Fixes: #634.
2019-04-05 15:34:39 +05:30
ViPuL
6b98a49245 webview: fix focus after soft reload.
Fixes: #697.
2019-04-04 20:35:31 +05:30
ViPuL
8e5c326d74 context-menu: Add option to copy zulip url.
Added, "copy Zulip URL" in 
* Context menu in left sidebar 
* Application menu item

Fixes: #649.
2019-04-04 00:35:23 +05:30
Rhythm Sharma
862e9e2c8c DND: Fix text for Toggle DND in sidebar on hover. 2019-04-04 00:21:59 +05:30
Akash Nimare
8a0b047d8f snap: Update snap config.
Updating the config as suggested by the snap community here - 
https://forum.snapcraft.io/t/snap-fails-to-load-error-minidump-segmentation-fault/8691/5

Should improve #593.
2019-04-03 16:37:33 +05:30
Akash Nimare
4a833ef603 docs: Fix typo error in issue template. 2019-04-02 16:13:44 +05:30
ViPuL
fefb7247d8 domain-util: add requestOptions to replace request instances.
This simplifies the request instances.

Fixes #603.
2019-04-02 16:11:23 +05:30
ViPuL
54a6903236 sidebar: Fix organization cycling using Ctrl+Tab.
This fixes two issues -
a) While on the first server, Ctrl+Shift+Tab doesn't work. This fixes that and now cycles to the last active webview.
b) Disable org cycling for non-server pages likes settings.

Fixes #691.
2019-04-01 17:34:08 +05:30
Rhythm Sharma
92ef1bd532 settings: Modify reset app data button. 2019-03-23 16:20:34 +05:30
Kanishk Kakar
bf2e04b3bb sidebar: Switch to next server on Ctrl+Tab.
* Adds a new option to the Window menu that allows users to change to the
next organization cyclically. Activated by Ctrl + Tab.
* Switch to the previous server on Ctrl + Shift + Tab.
2019-03-20 22:15:25 +05:30
ViPuL
3e389ea5db requests: Allow insecure requests on user request.
In certain requests like for fetching realm icons, request tries to verify server certificate and fails when the user has chosen to ignore cert warnings and has not uploaded the cert.
In those cases, we should mark that this server verification is ignored and ask request to ignore the cert verification bypassing that parameter.

Fixes #684.
2019-03-18 23:18:06 +05:30
SankalpSh
8edff28daa changelog: Fix grammatical errors. 2019-03-13 14:04:44 +05:30
Kanishk Kakar
ef6abbf49e settings: Default to starting app on login.
Acts on an observation that it is easier to turn off auto-start on login
than it is to turn on.
2019-03-12 20:35:20 +05:30
Ján Hrnko
0f638dd4b5 docs: Fix broken link.
Fix broken link http://jlord.us/essential-electron/ to https://jlord.dev/blog/essential-electron
2019-03-02 23:33:54 +05:30
Akash Nimare
92f0e46927 New beta release v2.5.0. 2019-03-01 18:51:38 +05:30
Rhythm Sharma
a0dd3832c2 settings: Shorcut section horizontal alignment fix. 2019-02-18 17:11:27 +05:30
ViPuL
a2e84595c9 pdf-viewer: disable pdf-viewer window.
Disable the pdf window for now since this is broken in v3
of electron.

Fixes #640.
2019-02-09 14:04:50 +05:30
Kanishk Kakar
9e33ebae62 context-menu: Fix context menu indexing.
Addresses a problem where the context menu would get the incorrect index
of server in sidebar and cause a crash when attempting to remove the
server.

Add comment justifying extra index parameter.
2019-01-30 12:22:36 +05:30
Abhigyan Khaund
9bdc5dd9e4 certificate: Use path.basename to get certificate file name. 2019-01-24 11:15:40 +05:30
Puneeth Chaganti
ac89ac1cb4 Browser-window: Change the window title to contain active Realm's name. 2019-01-23 13:35:18 +05:30
Abhigyan Khaund
31da1131a4 certificate: Use path.sep for path separator to support Windows.
Fixes the certificate path issue on Windows.
2019-01-21 21:44:23 +05:30
Priyank Patel
4980c71e5a notification: Ensure backward compatibilty when using narrow.by_topic.
We should ensure backward compatibilty when using narrow.by_topic since
the recent rename in the webapp of the function narrow.by_subject to
narrow.by_topic is only in master and not in any stable release yet it
could break the notification function for many people. Furthermore this ensure
general backward-compatibilty.
2019-01-08 21:57:13 +05:30
Akash Nimare
3e73511357 notification: Fix reply from notification.
The function "by_subject" is now being updated to the "by_topic" in
the webapp.

Fixes: #624.
2019-01-08 11:30:57 +05:30
Akash Nimare
5f23c8570b security: Update dependencies to fix minor dev security alerts. 2019-01-07 18:10:41 +05:30
Akash Nimare
12f79e18a1 test: Update test config files. 2019-01-07 12:27:20 +05:30
Akash Nimare
ad7f760444 windows: Install per user on Windows.
The official docs of the electron-builder says to install
the app per user on a particular system instead of all users.
2019-01-07 12:21:16 +05:30
Kanishk Kakar
7314c1f1dd context-menu: Fix Context Menu not working properly.
This fixes a case where the context menu doesn't work as expected. This used to happen when we
show the character icon.

Fixes: #608.
2018-12-30 20:48:38 +05:30
Akash Nimare
46c3d352a1 release: 🎉 new release v2.4.0. 2018-12-24 23:11:01 +05:30
Akash Nimare
cef8eadc8f sentry: Update electron-sentry to v0.14.0. 2018-12-24 23:08:49 +05:30
Akash Nimare
d3f742719c builder: Update electron builder to v20.38.4.
Also, updated electron-updater to v4.0.6.

Fixes: #618.
2018-12-24 20:17:18 +05:30
Priyank Patel
171d88755c electron: Update electron to v3.0.10.
* dependecies: Upgrade electron to v3.0.10.

This also makes sure we don't pass properties on this.tabs
that makes ipcRenderer crash on v3.x.x.

* tray: Use new way for using nativeImage.createFromBuffer.

The previous way of passing scaleFactor as a second argument
is now deprecated and will be a breaking change in 4.0

* app: Use new requestSingleInstanceLock api and remove deceprated makeSingleInstance.

* Update node-mac-notifier to v1.1.0.
2018-12-22 19:44:44 +05:30
ThePiyushGupta
09ac1bd338 setting: Minimize to tray on startup.
This hides the taskbar icon if the start to minimized setting is turned on.

Fixes: #565.
2018-12-22 19:19:24 +05:30
Akash Nimare
48dd12b738 request: Send user-agent with request.
We now send the user-agent with the request.
Fixes #611.
2018-12-19 12:23:18 +05:30
Akash Nimare
61bbd1cf22 electron: Update electron to v2.0.15. 2018-12-17 13:25:34 +05:30
Akash Nimare
bf722da390 git: Ignore unnecessary binary files. 2018-12-12 15:42:59 +05:30
Akash Nimare
c24f5b3e45 lint: Add eol for linebreaks on windows. 2018-12-10 18:36:44 +05:30
Akash Nimare
3d0f4d88af settings: Increase width of add a new org button. 2018-12-07 15:05:11 +05:30
Akash Nimare
a2f412c3de Improve wording of adding a new org button. 2018-12-06 18:14:55 +05:30
Akash Nimare
08559c2f4a setting: Improve organization page.
This adds a new button in the connected organization
page by which a user can quickly add a new Zulip organization.

Fixes: #607.
2018-12-05 23:00:07 +05:30
Akash Nimare
1f4509a6d2 codebase: Fix a typo. 2018-12-05 20:01:44 +05:30
Akash Nimare
06c8b2e8a2 context-menu: Fix context menu not working on adding new org.
Two changes -
* Fixes the context menu not working on adding a new org. This is
because previously the context menu listner only called when loading
the app. Updated the same to fix the issue.
* Refactor the codebase for left-sidebar.

Improves #599.
2018-12-05 19:55:56 +05:30
Kanishk Kakar
2aa15ee11a context-menu: Add context menu in left sidebar.
This adds a context menu in the left sidebar. For now, there is one sub-item called - Disconnect organization by which a user can delete an added org from the app.

Fixes: #599.
2018-12-05 19:42:47 +05:30
Akash Nimare
3c676672ec shortcut: Document show sidebar shortcut properly.
Fixes: #606.
2018-12-05 13:42:40 +05:30
Abhigyan Khaund
c937317ecf domain-util: Include certificates in all requests for icon.
Fixes: #463.
2018-12-05 12:43:34 +05:30
Akash Nimare
44dceda50e dependency: Update electron-window-state to v5.0.3.
Fixes: #231.
2018-11-30 21:37:02 +05:30
Akash Nimare
e1407cb6f7 dependency: Update electron-window-state to v5.0.2. 2018-11-22 20:37:19 +05:30
Michel Tomas
6899a6bc20 network: Fix request ecdhCurve mismatch errors.
The HTTP Node now uses auto for ecdhCurve for SSL connections. This fixes the SSL
handshake error while connecting to some Zulip instances. Setting the ecdhCurve to auto
is the recommended method for Node > 8.5, more info here -
https://github.com/nodejs/node/issues/16196

Fixes: #594.
2018-11-19 16:11:23 +05:30
Akash Nimare
925fec71d5 Add release notes for v2.3.82. 2018-11-05 00:24:25 +05:30
Akash Nimare
f847c565f9 menu: Update menu items on setting page.
This PR adds a functionality to update the menu items.
Some menu items like logout, shortcut etc are not needed
on setting page. We can control the same using this flag.

Fixes: #587.
2018-10-28 15:11:40 +05:30
Kanishk Kakar
c2a380d308 setting: Disable beta updates if auto updates disabled.
* Disable beta updates if auto updates disabled
2018-10-27 23:22:14 +05:30
Akash Nimare
ac6b206583 sentry: Update sentry to v0.12.1. 2018-10-24 19:27:38 +05:30
Akash Nimare
988405c665 dependency: Update node-json-db to v0.9.1. 2018-10-24 14:07:07 +05:30
Akash Nimare
a209804692 setting: Set custom css to false by default. 2018-10-23 15:09:28 +05:30
Akash Nimare
95c4df1898 network: Fix typo in network error message. 2018-10-23 14:56:56 +05:30
Rishi Gupta
1048c91d9e about: Remove Found bug button. 2018-10-21 05:05:44 +05:30
Akash Nimare
d723c5cd1c proxy: Do not reload app when turning off the SPS setting. 2018-10-17 16:58:26 +05:30
Akash Nimare
dcc7cd118c release: 🎉 new release v2.3.82. 2018-10-11 21:53:25 +05:30
Abhigyan Khaund
d2649dd5d7 native-menus: Add tools submenu and reword a few options.
Check for Updates → Move to Tools
Reset App Settings → Move to Tools, rename “Factory reset”
Both Dev Tools → Move to Tools
Download App Logs → Move to Tools
What’s new → Move to Tools, and rename “Release notes”.
2018-10-10 13:53:25 +05:30
Akash Nimare
25e6f6d482 sentry: Update Sentry to v0.11.0.
This could potentially fix #577 since the issue is caused by Sentry.
2018-10-09 13:51:12 +05:30
Akash Nimare
bd805b8324 native-menus: Rename a few options of the submenus. 2018-10-07 19:17:31 +05:30
Abhigyan Khaund
e1d98e0d8e native-menus: Rename a few options of the submenus. 2018-10-07 17:41:57 +05:30
Akash Nimare
89e5d2892b readme: Replace the word team with organizations. 2018-10-04 00:37:49 +05:30
Akash Nimare
ab28b56377 tray: Remove about menu item and rename focus. 2018-10-03 20:47:24 +05:30
Abhigyan Khaund
71aa7e28ba readme: Replace word team with organizations. 2018-10-02 13:50:57 +05:30
Akash Nimare
ba2bfba459 settings: Rename custom css section. 2018-10-01 18:10:40 +05:30
Akash Nimare
d181b0c2e5 settings: Move custom css section to advanced. 2018-10-01 18:07:20 +05:30
Akash Nimare
bda5c62928 settings: Put shortcut tip on the top. 2018-10-01 17:53:11 +05:30
Akash Nimare
b2b4fd1003 settings: UI fixes in general settings. 2018-09-28 21:03:49 +05:30
Akash Nimare
19770f5fbf settings: Update wordings of org settings. 2018-09-28 13:28:47 +05:30
Akash Nimare
4592396c4b settings: Update wordings of general settings. 2018-09-28 13:18:06 +05:30
Akash Nimare
19d3c0f5b5 left-sidebar: Update the border-radius of org icon.
Org icon now have rounded squares.

Fixes: #574.
2018-09-28 13:06:19 +05:30
Akash Nimare
af87789c7f feedback: Upgrade send feedback to latest. 2018-09-27 19:43:18 +05:30
Priyank Patel
36e710dbfe gitattributes: Mark package-lock.json files binary.
So they don't show up during `git diff`.
2018-09-26 20:46:36 -04:00
Priyank Patel
a6e1c93d81 dependencies: Upgrade send-feedback module to v1.0.8.
Fixes: https://github.com/zulip/zulip-electron/issues/569
2018-09-26 20:46:36 -04:00
Akash Nimare
684d88dd7f menus: Rename a few menu items. 2018-09-26 13:44:51 +05:30
Akash Nimare
45aedbd9db tray: Add a separator in tray before quit item. 2018-09-26 13:44:01 +05:30
Akash Nimare
6f411a819c menu: Remove whitespace from app version. 2018-09-26 12:27:09 +05:30
Rishi Gupta
097738332b tray: Remove separators. 2018-09-25 12:43:32 -07:00
Rishi Gupta
b777f8a50f menus: Rename a few menu items. 2018-09-25 09:51:01 -07:00
Akash Nimare
50f06239bb Add release notes for v2.3.8. 2018-09-25 18:58:19 +05:30
Akash Nimare
94cbc786d6 release: 🎉 new release v2.3.8. 2018-09-24 16:45:50 +05:30
Akash Nimare
75abf38515 sentry: Update Sentry to v0.10.1. 2018-09-24 16:45:50 +05:30
Akash Nimare
c1af019182 sentry: Add ignore errors to sentry configuration.
We could ignore the validate realm  error since it's harmless and we know the reason behind this.
It mostly comes from the console logs. This is a temp solution until Sentry supports disabling
the console logs.
2018-09-24 16:43:32 +05:30
Akash Nimare
1ea8aea22a sentry: Update Sentry to v0.9.0. 2018-09-21 14:50:47 +05:30
Akash Nimare
a7e418885a Merge branch 'master' of github.com:zulip/zulip-electron 2018-09-20 23:11:19 +05:30
Akash Nimare
0ac823c85b Update package-lock.json. 2018-09-20 23:10:47 +05:30
Akash Nimare
1096188ddf Add v2.3.7-beta release notes. 2018-09-20 23:06:49 +05:30
Akash Nimare
8d0b68316c menubar: Auto hide menubar on Windows/Linux.
Fixes: #563.
2018-09-19 15:09:39 +05:30
Akash Nimare
c45ad7096f menubar: Document auto-hide menubar setting. 2018-09-18 17:59:51 +05:30
Akash Nimare
5ca7937e52 menubar: Fix menubar not hiding properly. 2018-09-18 17:36:55 +05:30
Akash Nimare
481b99a2ab menubar: Fix a bug in menubar settings. 2018-09-18 13:47:16 +05:30
Akash Nimare
7eb4adf722 menubar: Update menubar on changing the autoHideMenubar setting. 2018-09-18 13:41:47 +05:30
Akash Nimare
4d21a8f8d8 menubar: Auto hide menubar on Windows/Linux.
This PR adds a setting option to auto hide the menubar.

Fixes: #563.
2018-09-18 12:32:23 +05:30
Akash Nimare
94270af265 settings: Minor changes in font-color. 2018-09-17 21:30:50 +05:30
Akash Nimare
177bff6113 UI: Clean up design of setting page.
This commit cleans up the design of setting page and
remove the inconsistency in the page design.

Few changes -
* Update the border-radius to 4px for all the buttons
* Better button UI
* Improve the layout of the setting page
* Cleaner toggle switch
2018-09-17 16:42:23 +05:30
Akash Nimare
b4fa4cacb6 electron: Update electron to v2.0.9. 2018-09-14 22:47:38 +05:30
Akash Nimare
06abf6716a Add dev into master. 2018-09-14 19:19:19 +05:30
Priyank Patel
0ad4ea4fdf left-sidebar: Add initial character of Realm Name instead of default icon.
This PR adds a feature for showing the first char of realm name as the server icon when the
icon fails to load for some reasons.
2018-09-14 18:18:45 +05:30
Akash Nimare
2ece432d19 Linux: Add label for help menu item. 2018-09-13 14:31:36 +05:30
Akash Nimare
10cd7850ca linux: Fix ALT+SHIFT opening menu items on Linux.
It's an upstream bug where if you press ALT+SHIFT on
Linux, it opens up the app menu items. The fix is adding
the & to all the menu labels.

Fixes: #528.
2018-09-13 13:13:18 +05:30
bemyak
86e3d06e52 Improve tray icon behavior. 2018-09-12 18:25:55 +05:30
Abhigyan Khaund
6ebc9acbf4 realm name: Update sidebar realm name when it's changed in webapp.
This commit updates the realm name in the sidebar tooltip,
domains.json and Window menu when it is updated in the server
through electron_bridge.
This commit also removes the server.alias parameter from the
tooltip onHover function and inserts it in the innerHTML of the
tooltip DOM.

Fixes: #425
2018-09-11 21:03:33 +05:30
Akash Nimare
278dc686e1 file-attachments: Allow multiple downloads of same file name.
Previous flow used to overwrite the file if it has the same name.
Current flow is same as what Chrome uses (though we use timestamp
in the file name rather than increaments).

Fixes: #558.
2018-09-10 17:49:09 +05:30
Akash Nimare
cec98c030e lint-xo: Update the max lines in a file. 2018-09-07 01:26:05 +05:30
Akash Nimare
f10d6eb115 setting: Update silent setting option.
Correctly update the audio of the webview on toogle-silent event.
2018-09-07 01:23:17 +05:30
Akash Nimare
8c5e928e9c settings: Clean up toggle sidebar setting.
This removes the two copies of the same toggle-sidebar
ipc event.
2018-09-07 01:20:49 +05:30
Akash Nimare
8d142c8c3f ipc: Move IPC listners to different function.
Those IPCs were wrongly put in the activateTab function.
2018-09-07 00:56:19 +05:30
Akash Nimare
a09769c560 release: 🎉 new beta release v2.3.7-beta. 2018-09-03 15:30:35 +05:30
Abhigyan Khaund
bb99015fd2 config-util: Use package reload instead of custom reload.
This is a WIP commit to fix the errors while saving settings that occur from
reloadDB function.
2018-09-03 15:25:07 +05:30
Akash Nimare
24f5c9b226 builder: Update electron-builder to v20.28.3. 2018-09-03 15:25:07 +05:30
Akash Nimare
dc6c4be5fe updater: Update electron-updater to v3.1.2. 2018-09-03 15:25:07 +05:30
Akash Nimare
50647e330b server-name: Unescape server name in window menu item.
Escaping is necessary to avoid any security risk but we need
to unescape those strings in order to show them in the frontend
otherwise it will have ugly special characters.

We already escape server name in the db and unesacoe it in
the left-sidebar. This PR adds the decodeString function in
order to unescape strings in the menu items.

Fixes: #554.
2018-09-03 15:20:02 +05:30
Rishi Gupta
73dc3db436 readme: Add tray/dock integration to features. 2018-09-01 23:15:39 +05:30
Akash Nimare
09cf21bf49 pdf-viewer: Hide menubar in pdf window.
We don't want to show the menubar in a pdf window
so setting it to null.
2018-08-30 21:32:31 +05:30
Akash Nimare
c30d0cc77b Add Zulip chat badge. 2018-08-30 17:40:42 +05:30
Rishi Gupta
872ad4d3e7 Update README.md. 2018-08-30 17:38:07 +05:30
Akash Nimare
6fd9e1be8b pdf-viewer: Add a feature to show the pdf files.
This adds a feature of showing the pdf attachments in a
new window so that a user can quickly view the same.

Fixes: #547.
2018-08-29 23:19:38 +05:30
Harmon
76c7f24161 settings: Update reference to toggle sidebar shortcut in general section. 2018-08-27 08:49:48 -07:00
Akash Nimare
f9c270492c Fix a typo in changelog. 2018-08-27 15:00:09 +05:30
Akash Nimare
371c580934 Add v2.3.6 release notes. 2018-08-27 14:59:11 +05:30
Akash Nimare
3eec4c2209 release: 🎉 new release v2.3.6. 2018-08-23 20:10:18 +05:30
Akash Nimare
debbfb6b7d electron: Update electron to v2.0.8. 2018-08-23 18:56:36 +05:30
Akash Nimare
8bd1492586 left-sidebar: Do not escape realm name.
We escape the realm name whenever user adds a realm + on app startup.
That's why we don't need to do the double escaping for already added
servers.

Fixes: #541.
2018-08-23 18:48:06 +05:30
Akash Nimare
1115c6d5c3 docs: Remove help docs from the repo.
Content is now maintained in the /help docs.

Fixes: #543.
2018-08-23 12:32:25 +05:30
Abhigyan Khaund
9ba279213c proxy: Add proxy details in request module parameters.
This commit adds proxy details to request module paramters from
the proxyRules so that the request module can use these rules while
sending a request. In case of no system proxy, set environment
variable NO_PROXY to handle all links.

Fixes: #534.
2018-08-21 00:26:37 +05:30
Akash Nimare
89c35cb1d4 electron: Update electron to v2.0.7. 2018-08-17 14:16:00 +05:30
Akash Nimare
21d6eb52c5 sentry: Update Sentry to v0.8.1.
This fixes the youtube video not playing in the lightbox issue.
Youtube video stopped playing in the lightbox when we added the
Sentry support. The exact reason behind the issue is still unknown
but we're guessing that previous version of Sentry doesn't exit
process on oncaught errors which might have caused lightbox to break.
The issue was fixed in the latest release of the Sentry v0.8.1.

Fixes: #537.
2018-08-14 18:02:10 +05:30
Akash Nimare
aa1538837b Add v2.3.5 release notes. 2018-08-03 17:39:48 +05:30
Akash Nimare
ea103380b6 release: 🎉 new release v2.3.5. 2018-08-02 20:27:29 +05:30
aashish-ak
60d10d88d1 preload: use page_params only when it exists.
This PR adds a params-util.js file which checks wheather the
page_params exists or not.

Fixes: #517.
2018-08-02 20:00:42 +05:30
Abhigyan Khaund
124a842bbd electron-bridge: Implement electron bridge.
This PR adds a bridge to communicate with the webapp in real time. As of now, the bridge listens for following events -

* When realm name changes
* When realm icon changes
* When the unread count changes

Partially fixes #425.
2018-08-02 01:05:29 +05:30
Akash Nimare
7130103999 file-attachment: Update notification message. 2018-08-02 00:11:47 +05:30
Akash Nimare
26a144e1c2 Merge branch 'master' of github.com:zulip/zulip-electron 2018-08-01 23:50:21 +05:30
Akash Nimare
a5c1ae8726 file-attachment: Add a setting option to show downloaded file in file manager. 2018-08-01 23:50:02 +05:30
Abhigyan Khaund
ed5096840d attachments: Fix failing attached file downloads.
This commit fixes the failing download files that had occurred due to the session not being the same in the browserWindow and the webview. This made the uploaded files unavailable to browserWindow for download. This fix adds the persist session to the browserWindow.

Fixes: #523.
2018-07-31 19:38:39 +05:30
Abhigyan Khaund
3f6d256910 gulpfile: Update syntax and methods for gulp v4.x.
This fixes the broken e2e-test.
2018-07-25 16:14:04 +05:30
Akash Nimare
28421992ba Update changelog.md 2018-07-24 19:31:25 +05:30
Akash Nimare
82199dd1c3 release: new beta release v2.3.4. 2018-07-23 23:16:18 +05:30
Akash Nimare
952baf1f42 electron: Downgrade electron to v2.0.1.
Looks like the latest release of electron has some
bugs and it's breaking the app. Downgrading until they
fix this in the upstream.
2018-07-23 23:12:03 +05:30
Abhigyan Khaund
3342d7da91 attachement: Handle downloading files failure.
In case of any failure, the app will fall back to the previous download
functionality which is to show a download dialog when a user clicks on 
file attachments.
2018-07-23 21:07:51 +05:30
Abhigyan Khaund
c0ec292090 fixes: Fix null of downloadPath when settings.json fails. 2018-07-23 21:04:49 +05:30
Akash Nimare
8f6fba0b97 electron: Update electron to v2.0.5. 2018-07-21 16:44:54 +05:30
Akash Nimare
d3453a3fe1 Update changelog.md 2018-07-14 21:31:24 +05:30
Akash Nimare
18e14fd3f8 Update changelog.md 2018-07-14 21:30:31 +05:30
Akash Nimare
c52bfd6a45 release: 🎉 v2.3.3. 2018-07-13 18:18:25 +05:30
Akash Nimare
428087110a tools: Add scripts to push/reset to pull request. 2018-07-13 17:05:46 +05:30
Abhigyan Khaund
781883a898 crash-reporter: Remove crash-reporter file and its use, as it is replaced by sentry. 2018-07-13 12:57:48 +05:30
Abhigyan Khaund
7299cd51e6 sentry: Initialize sentry for main and renderer process using logger-util.
captureExceptions are used to report expected exceptions from domain-util.js and
config-util.js.
All files that include logger-util automatically includes sentry for those files
and report unexpected exceptions to sentry.
2018-07-13 12:57:48 +05:30
Akash Nimare
a45254cff0 builder: Update electron-builder to v20.20.4. 2018-07-13 10:38:01 +05:30
Abhigyan Khaund
4095c121ba certificates: Make default option "No" on enter key press to reject certificate. 2018-07-12 20:32:33 +05:30
Abhigyan Khaund
6787b8d557 file-attachments: Download file-attachments to Downloads folder.
This PR adds a feature to download file-attachments to "Downloads" folder and shows a
notification when the download is complete. On clicking the notification user can
open the downloaded file with system default app. A user can set/change the download location
from the settings > general.
2018-07-12 18:39:43 +05:30
Akash Nimare
306e0f3b5e macOS: Add dock bouncing effect on macOS.
This PR adds a new setting option of controlling the
dock bouncing feature on macOS.

Fixes: #510.
2018-07-10 10:01:36 +05:30
Abhigyan Khaund
f030d5d56b package.json: Move adm dependecy to app package.json. 2018-07-06 17:00:57 +05:30
Abhigyan Khaund
acf7c5a238 error-logs: Create a zip file for app logs.
This PR adds the function that when a user clicks of "Show App Logs" from the menu, open the Downloads folder with a zip file containing all the Logs and User Configs. This will help the user to easily share logs with us.
2018-07-05 00:20:51 +05:30
Akash Nimare
da22f75300 builder: Update electron builder + updater. 2018-07-04 17:55:04 +05:30
Abhigyan Khaund
df35ffc3b0 config-files: Move all the configuration files into config folder. (#508)
This commit moves all the configuration(.json) files into a config folder
if the config folder doesn't already exist. It also updates the places
where these files are being accessed with the new address.
This will help us zip the folder easily in the future.
2018-07-04 14:46:13 +05:30
Abhigyan Khaund
eeb0b4a666 logger: Replace all instances of console.log with our logger-util. 2018-06-27 15:31:01 +05:30
Abhigyan Khaund
0a893c97c7 settings: Add an option to validate and add custom/self-signed certificates.
This PR helps to validate custom/self-signed certificates for servers
by saving the certificate file in certificates folder in user's appData folder.
We now use this certificate with the request while validating the server
when adding the organization. This validation of certificate is done by the request module itself.

Fixes: #126.
2018-06-22 12:50:20 +05:30
Akash Nimare
99a1711bb0 Open image-attachements in browser if inline preview is unavailable.
Fixes: #503.
2018-06-22 12:42:38 +05:30
Abhigyan Khaund
a27cf9e0e7 proxy-setting: Feature to use system proxy settings.
This PR uses resolveProxy to read system proxy settings and store
them in proper proxy format string using ConfigUtil. It removes the previous
use proxy option and replaces it with use system proxy and manual proxy
options.

Fixes: #296.
2018-06-15 23:40:41 +05:30
Abhigyan Khaund
22d6c6a2f3 config-util: Change return to a single return statement in isConfigItemExists.
Also add comments to describe the isConfigItemExists function.
2018-06-14 21:47:44 +05:30
Akash Nimare
c742591471 Use escape-html module for escaping realm name.
Since this module gives better results in compare
to window's default escape function.
2018-06-13 23:42:09 +05:30
Abhigyan Khaund
339a415d60 config-util: Add isConfigItemExists to check if a key exists. 2018-06-09 01:04:20 +05:30
Akash Nimare
f7696cc04d sidebar: Escape HTML for already added realm.
For the new servers we are already pushing the realm
details after escaping data but for already saved servers
we should escape the same to avoid any security issue.
2018-06-05 02:23:18 +05:30
Akash Nimare
1eabf5c5a2 Update settings error message. 2018-06-03 03:15:28 +05:30
Akash Nimare
4d020c639b Document error message properly. 2018-06-03 03:08:05 +05:30
Akash Nimare
85a6cf6890 Add changelog file. 2018-05-31 03:33:35 +05:30
Akash Nimare
ef1ad820aa release: new release v2.3.2. 2018-05-29 02:59:05 +05:30
Abhigyan Khaund
10f6d80e5f documentation: Change #electron stream links to #desktop stream link. 2018-05-25 21:44:57 +05:30
Akash Nimare
50ef02c324 Update package.json. 2018-05-24 04:01:45 +05:30
Akash Nimare
deed18d75f electron: Fix app not rendering colors properly.
There is a bug in v2 of electron which causes
pale colors. Resetting the color profile to srgb
fixes this problem. This is a temporary solution
until electron officially fixes this.

More info - https://github.com/electron/electron/issues/10732.

Fixes: #497.
2018-05-24 03:56:14 +05:30
Akash Nimare
0e3c6bceeb node: Update node to v8 on appveyor. 2018-05-22 01:03:54 +05:30
Akash Nimare
c2e138c16a tests: Temporarily disable e2e tests on appveyor. 2018-05-21 23:23:41 +05:30
Akash Nimare
8d1b027b0e tests: Temporarily disable e2e tests. 2018-05-21 21:52:23 +05:30
Akash Nimare
7bb8d78973 node: Update node to v8 on travis. 2018-05-21 04:08:58 +05:30
Akash Nimare
a7c7791bf8 new release v2.3.1. 2018-05-21 03:40:22 +05:30
Akash Nimare
5d120b4416 shortcuts-menu: Add a tip-section to link to webapp's built-in hotkeys. 2018-05-21 03:29:56 +05:30
Abhigyan Khaund
ceaf13dee2 shortcuts-menu: Add a tip-section to link to webapp's built-in hotkeys.
Fixes: #459
2018-05-21 03:27:30 +05:30
Akash Nimare
f81381dfec electron: Update electron to v2.0.1. 2018-05-21 03:18:18 +05:30
Akash Nimare
dbe89cdd09 security: Use proper method to escape html.
Now using the `escape-html` module so that we can decode
the realm description properly and escape the html at the
same time. The encodeURIComponent function doesn't
provide this kind of flexibility. We need to decode the
real description properly since we show the same in the
tooltip and setting page.
2018-05-21 03:11:03 +05:30
Priyank Patel
14c59bdae1 Update node modules.
* dependencies: Upgrade rc to the depth of 5.

This updates rc dependecies, to fix a vulnerability

deep-extend - Prototype Pollution
Refs:  https://nodesecurity.io/advisories/612

* dependencies: Upgrade stringstream to the depth of 6.

This solves, 5 vulnerabilities which were using the vulnerable
version of stringstream.

stringstream - Out-of-bounds Read
Refs: https://nodesecurity.io/advisories/664

* gulp: Upgrade gulp to v4.0.0.

This solves, 5 vulnerabilities.

minimatch - Regular Expression Denial of Service
https://nodesecurity.io/advisories/118

lodash - Prototype Pollution
https://nodesecurity.io/advisories/577

* dependencies: Update request to the depth of 6.

This solves 5 vulnerabilities.

stringstream - Out-of-bounds Read
https://nodesecurity.io/advisories/664
2018-05-21 02:17:18 +05:30
Akash Nimare
0ac3e3f6d3 New release v2.3.0. 2018-05-17 00:59:39 +05:30
Akash Nimare
d69c1339e6 security: Do proper HTML escaping for server data.
This commit fixes a security bug which was caused by
using innerHTML and not doing proper HTML escaping.
Ideally, we should be doing proper HTML escaping for
any data we get from the server to avoid XSS attack.
We already handle realm icon and url very well, the
realm description was not handled previously but this commit
now fixes this in a right way.
2018-05-16 18:25:03 +05:30
Akash Nimare
fe56a20334 setting: Add an option to disable the auto-updates. (#493)
Fixes: #491.
2018-05-16 16:00:21 +05:30
Akash Nimare
cfc97c9b73 Update electron-builder. 2018-05-15 19:01:33 +05:30
Abhigyan Khaund
2e70b515da menu: Add option to check for updates.
Fixes: #479.
2018-05-15 17:20:40 +05:30
Akash Nimare
51e414a508 windows: Update deprecated electron api (toPng -> toPNG). 2018-05-08 19:24:42 +05:30
Akash Nimare
8e7a9bf230 release: new beta release v2.2.0. 2018-05-08 03:23:52 +05:30
Akash Nimare
6493ddb8ec builder: Update electron-updater. 2018-05-08 03:21:53 +05:30
Akash Nimare
31edbe0d67 mac: Update dmg installer image.
Fixes: #474.
2018-05-08 03:17:31 +05:30
Abhigyan Khaund
9980fee785 loading-indicator: Fix loading indicator when server is loaded.
This PR changes the current implementation of the loading indicator by attaching
the indicator to the right dom element, so that it doesn't show up once a server
is loaded.

Fixes: #482.
2018-05-08 00:41:20 +05:30
Akash Nimare
ff9986ec6b crash-reporter: Remove deprecated autoSubmit api. 2018-05-03 04:15:20 +05:30
Akash Nimare
f3423d394c release: New beta release v2.1.0-beta. 2018-05-03 03:39:15 +05:30
Akash Nimare
a1da199627 Update app dependencies. 2018-05-03 03:09:53 +05:30
Abhigyan Khaund
537fbe8f9e feature: Add DND button in left sidebar.
This adds the do not disturb button to the left sidebar
which disables sound and notifications. It also disables
flash taskbar on windows.

Fixes: #298.
2018-05-03 02:35:13 +05:30
Akash Nimare
3fccb33fca electron. Update electron to v2.0.0. 2018-05-02 02:44:11 +05:30
Abhigyan Khaund
5638590c8b internal-links: Download file attachment links using downloadURL.
This commit download file attachments using downloadURL method of
webContent, the same way loadURL opens internal links. This
removes the use of hidden webview added in
f70432f4e3.

Improves: #469.
2018-05-01 20:37:33 +05:30
Priyank Patel
29ed00981d hidden-webview: Move hidden webview so it does't get deleted.
It turns out if you add/remove an org the hidden webview get deleted
since its in #webview-container where other sidebar webviews like which
get removed and readded through that process.

Improves: #469.
2018-05-01 06:09:36 +05:30
Akash Nimare
d7638c0b95 artifacts: Update artifacts name. 2018-05-01 04:14:40 +05:30
Akash Nimare
7fadbe877b electron: Update app dependencies.
electron: v1.8.6
electron-builder: v20.11.1
electron-updater: v2.21.8
2018-05-01 00:59:59 +05:30
Akash Nimare
32a21889fb snap: Update snap summary. 2018-04-20 18:55:45 +05:30
Akash Nimare
c4a961f9da snap: Update snap config. 2018-04-19 23:48:58 +05:30
Akash Nimare
ceaa898570 snap: Add source branch for snap. 2018-04-19 23:19:01 +05:30
Akash Nimare
73fe17041d snap: Update Snap configuration. 2018-04-19 22:11:33 +05:30
Akash Nimare
9f756cad3e snap: Update snap icon path. 2018-04-19 20:58:48 +05:30
Akash Nimare
6db6b7c482 reconnect-util: Do not throw error message on server page while reconnecting. 2018-04-18 22:00:30 +05:30
Akash Nimare
09c45e75e8 Update package-lock files. 2018-04-18 17:51:22 +05:30
Akash Nimare
120b80cf65 release: 🎉 v2.0.0. 2018-04-17 15:51:32 +05:30
Abhigyan Khaund
22f705960d sidebar: Add scrollbar for list of organizations on overflow. 2018-04-16 19:38:26 +05:30
Priyank Patel
ca8ce1deaa report-issue: Add report issue UX using send-feedback electron element.
This uses @electron-elements/send-feedback package to easily implement
UX for reporting issues.
2018-04-16 11:34:11 +05:30
Priyank Patel
f70432f4e3 internal-links: open non-image links in hidden webview.
* This will make sure that the current server webview will not 
reload due to URL change.
* Add an option to allow users to download the file attachments.

Improves: #469.
2018-04-11 23:21:01 +05:30
Abhigyan Khaund
60d693700e internal-links: Open image link in webapp lightbox.
This will open the image in the webapp lightbox. It shows the same behaviour
that happens when clicking on the image preview.

Improves: #469.
2018-04-09 18:16:57 +05:30
Akash Nimare
6e7333eab6 shortcut: Add F5 shortcut for reloading the app. 2018-04-07 19:52:46 +05:30
Akash Nimare
0d8dd1cd90 internal-links: Open file attachments within the app.
Changing the behaviour of opening the attachments in the
default browser to the app. This commit enables users to
download the attachments. If the attachment type is image
then we open the same in the app though users need to
click on the go-back button.

To-do - Open the image link in a lightbox.

Improves: #469.
2018-04-04 18:02:40 +05:30
Akash Nimare
0ee3757774 shortcuts: Update string for selecting the keyboard shortcuts node. 2018-03-30 14:56:17 +05:30
Akash Nimare
811df9f381 shortcuts: Remove unused shortcuts. 2018-03-30 01:56:48 +05:30
Akash Nimare
8bd02cc7e4 shortcuts: Update toggle sidebar shortcut to CMD/CTRL+SHIFT+S. 2018-03-30 01:53:07 +05:30
Akash Nimare
9d5d221371 test: Add a test for new org link. 2018-03-28 02:36:50 +05:30
Akash Nimare
6006f1a3f8 builder: Update electron-builder to v20.8.1.
Fixes: #442.
2018-03-27 21:27:32 +05:30
Robert
4f96df4a34 linux: Add Snap support for Linux.
This adds a new build configs in package.json for electron-builder
to be able to generate a snap package for zulip. 

Fixes #443.
2018-03-26 22:27:04 +05:30
Abhigyan Khaund
a13558fa16 org-settings: Responsive UI for connected orgs in smaller window sizes.
Fixes: #456.
2018-03-26 21:09:52 +05:30
Abhigyan Khaund
a1d19a385c menu: Warning dialog box for Reset App Settings. 2018-03-26 21:05:26 +05:30
Robert
c98667236e tools: Fix reinstall script for working across all platforms.
Make reinstall script compatible with Windows. 

Fixes #440.
2018-03-24 00:04:26 +05:30
Akash Nimare
b80c3d007b server-page: Responsive tweaks for divider. 2018-03-23 12:18:44 +05:30
Akash Nimare
70fb198a0b release: 🎉 new-release v1.9.0. 2018-03-23 03:59:56 +05:30
Abhigyan Khaund
7276bfeaa7 org-settings-page: UI fixes and tweaks for the org-settings page.
Fixes:
* Move "Disconnect" button to be at the far right.
* Ellipsis text-overflow for server-url.
* Show server url as title tooltip on hover.
* Remove settings-card's right transition on hover.

Improves: #456.
2018-03-23 03:45:50 +05:30
Akash Nimare
abd71330ba setting-page: Minor UI fixes for add new server page.
Minor fixes - 
* Update link to create a new organization.
* Update placeholder text for demo zulip server.
* Remove border from connect button.
* Add overflow for smaller window.
* Fix a typo in background color.
* Use buttons for server actions.
* Make new server page responsive.
* Focus buttons on tab.
* Update styling of button.
* Make add new server page responsive.

Fixes: #457.
2018-03-23 01:36:43 +05:30
Akash Nimare
e776222d6b browser-window: Prevent drag and drop events.
This stops a remote code execution via drag and drop event in
the main/renderer process.

Fixes #453.
2018-03-22 23:23:18 +05:30
Akash Nimare
30b05571e7 electron: Upgrade to v1.8.4.
This upgrade includes some webview security fixes.
https://www.electronjs.org/blog/webview-fix
2018-03-22 11:14:32 +05:30
Akash Nimare
0a155c63e7 security: Do not allow running insecure contents in app. 2018-03-22 10:57:35 +05:30
Akash Nimare
13c750ac6c tests: Update Spectron to latest. 2018-03-20 20:54:32 +05:30
Akash Nimare
48799f75d1 tests: Fix failing renderer tests. 2018-03-20 20:53:10 +05:30
Akash Nimare
29f4e702ad Merge branch 'master' of github.com:zulip/zulip-electron 2018-03-19 20:45:54 +05:30
Akash Nimare
3eb4cf4f64 Ubuntu: Add Zulip link to the binrary.
Zulip can't be run from the terminal because we were not
linking the binary to the bin. The electron-builder used to support
this by default but we as we added the afterInstall
script it got removed.

More info -
https://github.com/electron-userland/electron-builder/issues/2682,
https://github.com/electron-userland/electron-builder/issues/2689

Fixes #444.
2018-03-19 20:38:38 +05:30
Abhigyan Khaund
1a97d8a5b0 design: Improve connected organization design.
This PR updates the connected organization page.
Changes - 
* All the server details are now inline. 
* Change "Delete" to "Disconnect". 
* Club org logo and name together on the right, and URL and "Disconnect" to the left.
* Link to organization logo on the server tab.
 
Improves #340.
2018-03-19 17:10:42 +05:30
Akash Nimare
2f96ec6199 settings: Fix a typo in silent setting option. 2018-03-17 03:49:43 +05:30
Akash Nimare
52de465457 settings: Fix app settings overriding issue. (#449)
This fixes an issue which was caused by saving the last active tab
before a functional tab activates. The bug was introduced in f409bb0449.
It was unnoticed from v1.5.0 to the latest v1.8.2.

The bug causes a serious issue where the app can't change the app settings and all settings were
getting overridden when a user switches back to setting page.

Fixes #448.
2018-03-17 03:06:41 +05:30
Ganesha Danu Enastika
e3039cf5a9 tools: Add missing Ubuntu dependancy. 2018-03-14 23:43:07 +05:30
Akash Nimare
6c120269eb design: Redesign Add new server page. (#446)
Changes - 
* New setting nav item for connected organizations.
* All new page for adding a new server.
* Minor UI changes in setting page.
* Update default height, width of the main window.
* Fix webview showing outline.
* Hide Server nav item in setting page.

Fixes #340.
2018-03-14 23:41:12 +05:30
Akash Nimare
b31fc6b66d setting-page: Better looking nav items. 2018-03-10 23:14:42 +05:30
Akash Nimare
435e5f086e webview: Fix webview flickering. (#439)
Rewrote styling for the webview so that it takes the whole window
when the app loads up.

Fixes #249.
2018-03-09 14:35:13 +05:30
Akash Nimare
603ad7dfcd setting-page: Make setting navs better. 2018-03-07 21:00:30 +05:30
Akash Nimare
a47a0e3e90 server-page: Improve add new server page.
WIP, #340.
2018-03-06 17:45:00 +05:30
Priyank P
8e04920f54 il8n: Add locale helper script. (#437)
This script automatically builds locales if the locale-template is changed.
2018-03-06 05:08:24 +05:30
Abhigyan Khaund
4a7b84e483 menu: Add "What's new" in help submenu. 2018-03-06 04:49:51 +05:30
Akash Nimare
ffd2ee36fa left-sidebar : Fix position of left-sidebar on toggel. 2018-03-01 21:36:13 +05:30
Akash Nimare
c11fa93642 left-sidebar: Fix tooltip not visible on hover.
Regression from c11089027c.
2018-03-01 21:12:07 +05:30
Akash Nimare
a90dc0c82f Custom css: Add a setting option for custom css.
This PR adds an option to inject custom CSS.
Fixes - #432.
2018-03-01 18:52:53 +05:30
Akash Nimare
028bc02d0a Downgrade node to 6.
Looks like the app can't be packaged on node > 8. This must be an issue with electron-builder.
Downgrading until the fix comes.
2018-03-01 04:37:00 +05:30
Akash Nimare
d673d5b76c 🎉 v1.8.2. 2018-02-26 19:18:23 +05:30
Akash Nimare
2bf88aa912 webview: Remove loading indicator once webview is loaded. 2018-02-26 18:36:25 +05:30
Akash Nimare
c9f179a2ae node: Enforce LTS node v8.0.0. 2018-02-26 18:28:16 +05:30
Akash Nimare
c11089027c left-sidebar: Align action container properly. 2018-02-26 18:09:42 +05:30
Abhigyan Khaund
5c45ab7b66 fix: New organization link overlapping existing servers.
Fixes: #427.
2018-02-26 17:28:59 +05:30
Akash Nimare
6205ca0aca Update shortcut for opening webapp keyboard shortcuts. 2018-02-24 01:27:28 +05:30
Abhigyan Khaund
b83e2dd428 settings-tab: Do not create a sidebar tab rather highlight bottom settings icon. (#422)
Creating the new functional tab for setting tab was unnecessary as we already have a sticky setting icon at the bottom. This PR adds a functionality to highlight that settings icon instead of creating the new one.

Fixes: #418.
2018-02-23 19:03:30 +05:30
Akash Nimare
fd421a62d2 webview: Improve flickering of webview on first load. 2018-02-23 02:03:18 +05:30
Abhigyan Khaund
fa85241c79 design: Only show the back button when needed.
Fixes: #421.
2018-02-23 00:53:23 +05:30
Akash Nimare
b163c237b6 Do not auto-reload app when system comes back from sleep.
Improves #412, #312.
2018-02-22 20:55:55 +05:30
Abhigyan Khaund
5351ee10fa auto-updates: Show a notification when a new update available [Linux].
[Linux] - This PR adds a feature to notify the user whenever a new update is available.

Fixes: #398.
2018-02-22 16:55:45 +05:30
Priyank P
d48b6ae80d reconnect: Check wheather internet is working before reloading. (#415)
When the online event is triggered check whether the internet is actually working or not.
Commonly on windows, it turns out that internet takes couple of seconds to boot up after
connecting to the internet or in some cases, this might be they have to sign in to internet service
portal in order to access the internet.
2018-02-21 16:56:01 +05:30
Akash Nimare
2d07d40c92 activetab: Fix broken last active tab. (#420)
This fixes an issue where the server tabs were not loading
according to the last active tab. This used to load last server
no matter what the last used server is.

Fixes - #416.
2018-02-18 17:52:26 +05:30
Akash Nimare
55ae71c00c travis: Fix travis not failing on linting errors. 2018-02-15 19:58:08 +05:30
cPhost
0c18bb5226 menu.js: Fix linting error. 2018-02-15 19:55:37 +05:30
Abhigyan Khaund
6d213c86a8 design: Add back button in left-sidebar.
Fixes: #208.
2018-02-14 14:01:35 +05:30
Akash Nimare
0c1cd96ed7 Remove transition property from checkbox input.
That delay can cause the setting button to not change/switch properly.
2018-02-13 17:23:39 +05:30
Priyank P
d77b1b5960 electron-connect: Disable electron-connect when it's run from npm start.
Improves gulp dev script.
2018-02-13 00:07:44 +05:30
Akash Nimare
2c6c0c8c8d Update electron to v1.8.2. 2018-02-11 22:36:32 +05:30
Akash Nimare
84bf59d32f Update package-lock.json files. 2018-02-11 22:08:50 +05:30
Priyank P
256c352fb9 windows: Add tools support with batch scripts.
Add tools support with batch scripts.

Fixes - #397.
2018-02-09 18:19:09 +05:30
Akash Nimare
da28589c17 menu: Add menu-item to reveal app logs in file manager #405. 2018-02-03 02:04:36 +05:30
Akash Nimare
c452ee2ef6 menu: Improve help menu. 2018-02-03 01:37:13 +05:30
Abhigyan Khaund
3582aa4694 menu: Rename Settings to Desktop App Settings. 2018-02-02 17:07:12 +05:30
akashnimare
7f7eee2455 setting-page: design improvemnets. 2018-01-31 21:36:39 +05:30
akashnimare
c716b8f233 notification: Refactor code for bot mention in reply. 2018-01-31 01:34:46 +05:30
Abhigyan Khaund
4a40c75127 notifications: Adds bots mention support to reply option. (#395)
Fixes: #391.
2018-01-31 01:26:11 +05:30
Balaji
239631a2b6 tools: Add scripts for review Pull Requests. (#399)
* tools: Add script to fetch-pull-request.

Improves #397.

* tools: Add script to fetch-rebase-pull-request.

Improves #397.
2018-01-30 17:11:10 +05:30
Abhigyan Khaund
89d1344e2f preference page: Add a Loading indication for new server button. (#401)
Change the text of "Add" button to "Adding..." when a user clicks on Add button for adding new server.

Fixes: #396.
2018-01-29 23:53:10 +05:30
akashnimare
1948ba2cc3 menu: Fix Zoom In shortcut.
This is a temporary fix. Ideally, 'CmdOrCtrl+Plus' works on all
the platforms but because of https://github.com/electron/electron/issues/6731
it converts accelerator to 'Ctrl+Shift+Plus'.
2018-01-27 19:56:27 +05:30
Abhigyan Khaund
b8da7dd6ee gulp-dev: Add nodeman to dev script and fix renderer reload on changes.
Fixes: #368.
2018-01-23 00:29:15 +05:30
akashnimare
4a0efb7301 Code refactoring. 2018-01-23 00:05:15 +05:30
Priyank P
aedd95259d preference: Only toggle the state if element is present.
Fixes #393.
2018-01-22 22:38:01 +05:30
Priyank P
c8d7a79877 design: Improve preference design. 2018-01-17 18:34:24 +05:30
akashnimare
6e6db42b54 v1.8.1 2018-01-17 02:16:04 +05:30
akashnimare
db79284fbb fix: Don't import appId from package file #386. 2018-01-17 01:54:20 +05:30
akashnimare
2434f06655 release: 🎉 new-release v1.8.0. 2018-01-15 20:59:28 +05:30
akashnimare
1d611d3382 setting-page: Update placeholder for adding new server page.
Improves #340.
2018-01-15 20:55:51 +05:30
akashnimare
a746194e9e help-menu: Add a space between app name and version. 2018-01-15 18:45:45 +05:30
akashnimare
7cc13f7a26 Update dependencies.
Updated:
electron - 1.7.10
electron-builder - 19.53.6
electron-updater - 2.18.2
2018-01-15 17:46:33 +05:30
Akash Nimare
6a9bb152a0 Update features section. 2018-01-14 01:11:18 +05:30
Priyank P
8b6dcd355f notification: Add reply option to notifications for macOS.
This PR adds reply option to notifications of macOS using
`node-mac-notifier` and then post the reply for to the webapp.
It also fixes an issue that even though the app is focused the server that sent
the notification did not focus. And it also adds parsing for mentioning. This also
refactors code for notification.

Fixes: #284, #381.
2018-01-14 00:04:34 +05:30
Priyank P
91742a5770 silent: Reflect changes in webview for silent option. (#380)
* silent: Reflect changes in webview for mute/silent option.

This silent the webview incase silent option is toggled, and
by default silent the webview when its create if needed.

Fixes: #380.
2018-01-11 18:46:39 +05:30
akashnimare
fb74251a2c performance: Disable hardware acceleration to decrease the load on GPU.
Adding this experimental electron api to see if it makes any difference
in performance.

Improves #213.
2018-01-08 02:10:43 +05:30
akashnimare
a920720f91 setting-page: Improve add new server page #340. 2018-01-07 15:10:21 +05:30
Priyank P
aa8e99b7a6 domain-util: Fix checkDomain, so it checks all error codes. (#370)
This fixes an issue where if server send non 404 error code such
as 403 forbidden we marked them as Zulip server even though they are
not, now it checks for 400 error range.
2018-01-07 00:16:52 +05:30
akashnimare
e23f8aaa58 shortcut: Rename Zoom In keyboard shortcut. 2018-01-06 16:30:59 +05:30
Akash Nimare
5c3208d44c settings: Add a setting option to start the app in background. (#366)
Fixes #314.
2017-12-29 12:37:53 +05:30
akashnimare
c0b57bbe2b settings: Set default value of flashtaskbar setting[Windows]. 2017-12-28 00:29:16 +05:30
Priyank P
afe4e8901b github templates: Add pull request template and update issue template. (#365) 2017-12-27 23:15:02 +05:30
Priyank P
231e7fd9c2 preferences page: Reflect changes in the preference page. (#362)
This updated the setting page if the sidebar was toggled using a shortcut.
This also updates the setting page if the tray was toggled using menu.

Fixes: #304.
2017-12-27 21:06:32 +05:30
Akash Nimare
a0d898a5b7 Merge pull request #363 from cPhost/logger
logger-util: Code refactoring and better logs design.
2017-12-27 20:45:13 +05:30
cPhost
1abf62555c logger-util: code refactoring and better logs design.
This imporves logging and refactors most of the code.
This also renames console-util to logger-util.
2017-12-26 22:03:05 -05:00
Akash Nimare
6befcbaa8f Merge pull request #361 from cPhost/focus-app
notifications: Focus app when a notification is triggered.
2017-12-27 01:08:32 +05:30
cPhost
e56a01049b notifications: Focus app when a notification is triggered.
This PR adds a feature of showing app window when the notification are clicked
Fixes: #358
2017-12-26 14:30:33 -05:00
akashnimare
72cb8459ff code refactoring. 2017-12-26 01:02:42 +05:30
Akash Nimare
0b83b22206 Merge pull request #353 from cPhost/fix-errors
default-util: Fix log dir errors.
2017-12-26 00:21:35 +05:30
Akash Nimare
267d25e5c4 Merge pull request #357 from cPhost/img-fix
Add default icon if the server image is not available.
2017-12-25 23:43:35 +05:30
cPhost
8401f8f5ce server icon: Load default icon if org icon is not avalible. 2017-12-25 12:17:45 -05:00
cPhost
c4a7264f34 console: Fix errors where Logs dir can't be created. 2017-12-25 09:50:52 -05:00
akashnimare
9d081ecd5a Reload full app on system hibernation.
Improves #312.
2017-12-20 02:33:08 +05:30
akashnimare
dc6582fa82 Logs: Unused Logs dir. 2017-12-19 03:42:42 +05:30
akashnimare
3b412672c6 Logs: Fix an issue where Logs dir don't get init properly. 2017-12-19 03:40:24 +05:30
Akash Nimare
04083bfa81 Merge pull request #352 from zulip/cPhost-handle-domainjson
Handle corrupted config files.
Improves #310.
2017-12-19 02:33:39 +05:30
akashnimare
562e82d2f1 test: Create logs dir on app startup. 2017-12-19 01:22:50 +05:30
cPhost
3b014e0715 settings util: delete settings.json file in case it is corrupted. 2017-12-17 15:17:08 -05:00
cPhost
13178ebc8f domain util: delete domain.json file in case it is corrupted. 2017-12-17 15:16:55 -05:00
cPhost
08693bf105 console: require app form remote if required 2017-12-17 15:00:04 -05:00
akashnimare
d7a0b63d62 test: fix failing e2e test on travis 2017-12-13 20:05:07 +05:30
akashnimare
a193ecf229 design: update new server page #340. 2017-12-13 18:54:31 +05:30
akashnimare
31f04754a4 design: fixed positioning of create-new-org link 2017-12-11 16:08:23 +05:30
akashnimare
ae7374475f design: Update UI for server section #340 2017-12-11 15:42:50 +05:30
akashnimare
7697d5d698 electron: update electron to v1.7.9 #213 2017-12-11 15:09:56 +05:30
akashnimare
d3e1b5de45 update app configs 2017-12-06 19:46:24 +05:30
Akash Nimare
9efa6191f7 Merge pull request #349 from zulip/improve-debian-installer
linux: Improve debian installer
2017-12-06 18:17:57 +05:30
akashnimare
d86797d2fc debian: replace bash shebang with '#!/usr/bin/env bash" for portability.
According to the debian config rules '#!/usr/bin/env bash' violates
the quality standard which isn't allowed.

More info - https://github.com/Microsoft/vscode/issues/35638
2017-12-05 23:58:26 +05:30
akashnimare
23eef7edb0 debian: remove app settings on uninstall 2017-12-05 22:15:18 +05:30
akashnimare
a1d5a35ccf debian: Remove config files when user uninstalls app 2017-12-05 21:01:26 +05:30
akashnimare
62e8dfe180 Rename afterRemove script to debian uninstaller 2017-12-05 20:54:39 +05:30
Akash Nimare
8322054984 Merge pull request #347 from cPhost/deb
debian-installer: add apt repo for debian
2017-12-05 18:42:15 +05:30
Akash Nimare
13ae6f07e9 Add comment for adding apt repo 2017-12-05 18:41:06 +05:30
cPhost
dab92be54c debian installer: remove the apt repo after unistallation 2017-12-05 07:29:45 -05:00
cPhost
fe9c66d8c2 debian-installer: add apt repo for debian 2017-12-05 07:29:34 -05:00
akashnimare
43b4d511dc init default settings properly fixes #348 2017-12-04 17:19:25 +05:30
akashnimare
aa5a47ad53 Make app window thinner fixes #332 2017-12-04 15:19:43 +05:30
Akash Nimare
ce27f92900 Merge pull request #346 from cPhost/about-page
about-page: add links for zulip-electron and license.
2017-12-04 14:54:13 +05:30
cPhost
7be051bb6e about-page: center everything, add links for zulip-electron, license 2017-12-03 18:02:34 -05:00
Akash Nimare
c2a01adabe Merge pull request #345 from cPhost/logger
logger: add console helper to log for both file and console
2017-12-04 01:13:07 +05:30
cPhost
770926e6eb logger: add console helper to log for both file and console 2017-12-02 11:58:28 -05:00
Akash Nimare
10ef627f59 Add notifications troubleshooting 2017-12-01 20:43:52 +05:30
akashnimare
4adba0f4b4 Update electron-builder to v19.46.4 2017-11-24 17:00:03 +05:30
akashnimare
ed590c26e3 Added whitelist zulip server 2017-11-23 22:38:28 +05:30
akashnimare
6d10291a87 🎉 v1.7.0 2017-11-22 20:10:59 +05:30
akashnimare
3fe3a3da85 Update electron to v1.6.15 2017-11-22 16:21:39 +05:30
akashnimare
ba64438a99 Improve Add Server page for new users [WIP] #340 2017-11-22 14:39:39 +05:30
Akash Nimare
bcc27894c4 Merge pull request #337 from SimplyAhmazing/setup-e2e-tests
setup e2e tests
2017-11-22 13:28:31 +05:30
Akash Nimare
0dd0f593d1 Merge pull request #338 from sroy8091/issue_#336
Better error message on invalid server fixes #336
2017-11-21 21:08:08 +05:30
Sumit Roy
f5e9342f78 fail to connect message #336 2017-11-21 11:31:42 +05:30
simplyahmazing
083ccdf229 remove unused imports 2017-11-20 11:14:22 -05:00
simplyahmazing
1261786db2 remove unused dependencies 2017-11-20 11:06:05 -05:00
simplyahmazing
7f567f55c3 setup e2e tests 2017-11-19 16:48:21 -05:00
akashnimare
dcd2abca6e v1.6.0-beta 2017-11-16 00:32:32 +05:30
Akash Nimare
2fb9efb981 Merge pull request #335 from zulip/electron-shortcut-fix
Remove electron-localshortcut completely
2017-11-15 16:56:42 +05:30
akashnimare
7245b6a110 Move electron-debug to devDependencies 2017-11-15 04:59:59 +05:30
akashnimare
bcb8ffb55f Remove electron-LocalShortcut completely
This commit removes the usage of "electron-localshortcut" completely.
Now, we rely on only menu accelerators for shortcuts. It's risky to register a local shortcuts in the app window
either using electronLocalShortcut or globalShortcut as the registered shortcuts could interfare with
OS global shortcuts which is very frustrating. This fixes #317 once and for all.
2017-11-15 03:39:51 +05:30
akashnimare
77094596a5 Use electron-debug in development only WIP #317
electron-debug hijacks the CMD/CTRL+R and reloads the whole app,
whereas we only need to reload the current server. Removed those commands from
electronLocalShortcuts as well as they are already registered in menu items.
2017-11-15 03:07:36 +05:30
Akash Nimare
06ad44bdd7 Merge pull request #333 from zulip/appimage-autoupdates
Add AppImage autoupdates fixes #333
2017-11-11 20:12:24 +05:30
akashnimare
e719ba139c Update electron-builder to latest version 2017-11-11 20:10:53 +05:30
akashnimare
9853e9226c Better tray icon for retina display, fixes #330 2017-11-08 12:26:52 +05:30
akashnimare
f2c76b5ca3 Enable auto-update on Linux (AppImage) 2017-11-04 01:31:45 +05:30
Akash Nimare
e6dbff995b Merge pull request #327 from zulip/taskbar-setting-option
Setting option for Windows taskbar flash fixes #299
2017-11-03 00:09:58 +05:30
akashnimare
4578d4a5f7 typo in setting 2017-11-03 00:06:34 +05:30
akashnimare
4b895a2312 Don't show flash taskbar setting on Linux/macOS 2017-11-02 23:54:38 +05:30
akashnimare
53c0428a3a Add setting to control Windows taskbar flashing #299 2017-11-02 20:58:35 +05:30
akashnimare
0a1866abb5 Show Detailed error message on invaild Zulip server #325 2017-11-01 17:59:00 +05:30
Akash Nimare
ce862a4890 Merge pull request #326 from zulip/remove-python-version-file
Remove python version file

Fixes failing tests on Travis Linux
2017-10-31 20:55:17 +05:30
simplyahmazing
1b1ad2cd61 remove .python-version file from repo 2017-10-28 20:06:56 -04:00
simplyahmazing
ead7a06308 ignore .python-version files 2017-10-28 20:06:26 -04:00
akashnimare
6659dd5097 Update electron-builder & updater to latest 2017-10-26 11:54:55 +05:30
akashnimare
ed1f0f6d5b Update electron-builder & updater to latest 2017-10-24 18:38:00 +05:30
akashnimare
79acf8a6e1 Add option to remove app settings
Menu item "Reset app settings" now remove all the configurations/settings files related to app.
Previously it used to remove only window-state.json. This helps a bit in #310.
2017-10-24 18:29:35 +05:30
akashnimare
8e0033f03e Handle certificate issuer error
Fixes, #316
2017-10-21 01:47:16 +05:30
Akash Nimare
9144c2630d Merge pull request #322 from zulip/spellchecker-osx-fix
Spellchecker Improvements
2017-10-20 23:52:24 +05:30
akashnimare
fae05fc3b1 Initialize default app settings
Settings are initialized only when user clicks on General/Server/Network section settings
In case, user doesn't visit these section, those values set to be null automatically.
This fix makes sure the default settings are correctly set to either true or false.
2017-10-18 21:38:48 +05:30
akashnimare
73603a4fd2 Add settings to disable/enable spellchecker 2017-10-18 21:36:01 +05:30
akashnimare
a498ffc7d6 Update spellchecker to v1.1.2 2017-10-18 04:17:51 +05:30
akashnimare
7afcf13401 Re-write and improve spellchecker class
Rewrote the Spellchecker class so that we can have better control
over the context menu and spellchecker.
2017-10-18 04:14:02 +05:30
akashnimare
89a292559d Set English as default language for spellchecker on Linux/Windows 2017-10-17 21:57:51 +05:30
akashnimare
be14517caf Set server language for spellchecker on macOS
Ideally spellchecker should detect the language, but on macOS, it fails to auto-detect the lanugage user is typing in
that's why we need to mention it explicitly. We set this language with the help of the default language of the server.
2017-10-17 00:29:50 +05:30
akashnimare
3b6c5ae532 🎉 v1.5.0 2017-10-11 12:47:17 +05:30
akashnimare
40e3ed0f2f Reload current view properly [WIP] 2017-10-10 16:35:43 +05:30
Akash Nimare
5d988858b0 Merge pull request #313 from YJDave/tooltip
Add tooltip for add server button
2017-10-10 15:40:24 +05:30
YJDave
3a974136a3 Tooltip for add organization icon 2017-10-07 14:08:26 +05:30
akashnimare
6ed5a5309c Load correct Active tab
We need to deactivate the tab if it doesn't match with previously loaded active tab.
2017-10-06 02:59:16 +05:30
akashnimare
80c37fabb8 Enable badge api on macOS only
app.dock.setBadge() is supported on macOS only. Added an extra check so that it doesn't throw errors on Win + Linux.
2017-10-05 05:34:16 +05:30
akashnimare
79366e19df Remove unnecessary logging 2017-10-05 05:28:26 +05:30
akashnimare
f409bb0449 Handle reload event correctly
Added the functionality to remember the last active tab. Previously, we used to load the first tab no matter what.
Also, when user adds a new server the same server will be activated.
Reloading the app will now reload the current view/server only.

Fixes #311, #308
2017-10-05 05:21:34 +05:30
akashnimare
45bdde951f Add a red circle over dock icon for PMs
This will show a small red circle over the dock icon.
This is to notify user that they have PMs in unread messages.
2017-10-03 03:29:52 +05:30
akashnimare
6b627780f0 Fetch correct organization icon from server_settings API
Some Zulip Servers use absolute URL for server icon whereas others use relative URL.
I have added an extra check to handle both the cases. Improves #308.
2017-10-03 00:11:41 +05:30
akashnimare
6f67553da5 update electron to v1.6.14 2017-10-02 22:01:28 +05:30
Akash Nimare
2e710a9322 Merge pull request #309 from zulip/org-server-icon
Show server-info on hovering the server-icons
2017-10-01 03:29:29 +05:30
akashnimare
91f3afa8fe Show server-info on hovering the server-icons 2017-10-01 02:51:50 +05:30
akashnimare
f784345495 Fix sidebar tooltip
This commit fixes an issue which was caused by the recent changes in left-sidebar styling.
Due to transform property the tooltip of action-buttons (reload, setting) was hidden on hover.
2017-10-01 02:06:08 +05:30
akashnimare
67da435154 Fix a typo in base notification 2017-09-30 03:34:19 +05:30
akashnimare
c89733610d Fix desktop notification control setting
This commit fixes a bug which was caused by calling the notification constructor without the args (title, opts etc).
2017-09-30 03:26:59 +05:30
akashnimare
8f272a67b5 Update desktop app installation link 2017-09-29 17:37:36 +05:30
Akash Nimare
f6c4a76138 Merge pull request #307 from aklap/change-menu-label
Rename menu label Zulip Desktop as About Zulip.
2017-09-27 18:01:22 +05:30
Alexis La Porte
b90a4c5254 Rename menu label Zulip Desktop as About Zulip.
This fix changes the menu label 'Zulip Desktop' to 'About Zulip' for clarity, ('About' is more descriptive of the content in the view associated with the label). Also to have the menu conform to convention regarding naming menu labels for Mac OS applications.

Fixes #306
2017-09-27 03:41:10 -04:00
akashnimare
a06e09e565 update electron-builder to v19.29.1 2017-09-22 15:28:33 +05:30
akashnimare
ad5bef821e UI: Remove sidebar fluctuation on switching the toggle button
Fixes #301
2017-09-21 00:48:08 +05:30
Akash Nimare
58bbd7bf30 Merge pull request #302 from zulip/revert-301-ui_enchancements
Revert "Removed the sidebar fluctuation on switching the toggle button"
2017-09-20 23:18:59 +05:30
Akash Nimare
90d080dc96 Revert "Removed the sidebar fluctuation on switching the toggle button" 2017-09-20 23:18:35 +05:30
Akash Nimare
ad3fcf585e Merge pull request #301 from Shipragupta14/ui_enchancements
Removed the sidebar fluctuation on switching the toggle button
2017-09-20 02:31:53 +05:30
Shipragupta14
4b8f216bab Removed the sidebar fluctuation on switching the toggle button 2017-09-20 02:03:39 +05:30
Akash Nimare
e620e0c428 Merge pull request #294 from ihsavru/master
Improve UI/UX of setting page
2017-09-19 17:11:18 +05:30
ihsavru
50b3151b5d corrected typos 2017-09-19 15:52:03 +05:30
ihsavru
0c32756485 changed button and switch colour 2017-09-19 10:44:39 +05:30
ihsavru
0c0835e364 changed shortcuts UI 2017-09-17 23:31:53 +05:30
ihsavru
9e962a5c44 improved toggle switches 2017-09-17 11:54:40 +05:30
ihsavru
a218f7ea64 change toggle buttons 2017-09-17 10:45:09 +05:30
ihsavru
13a7f7475a added toggle switches 2017-09-16 19:09:12 +05:30
ihsavru
48b17a1549 made settings page responsive 2017-09-16 17:39:07 +05:30
akashnimare
653598fd9e add a re-install script 2017-09-16 01:54:55 +05:30
Akash Nimare
ddbc282f49 Merge pull request #297 from cedricium/create-new-org
Added 'Create New Organization' link to the Settings page
2017-09-15 21:17:15 +05:30
Cedricium
992d92b06d Changes made based on review
Removed unnecessary comments and changed the 'Save' button in `new-server-form.js`
to 'Add'.
2017-09-15 08:19:39 -07:00
Cedricium
45867ef15e Made changes based on @rishig recommendations
Changes include:

- increasing the font size of the 'Create new organization' link
- adding more bottom margin to the link
- aligning the link text with the external navigation icon
2017-09-14 20:49:40 -07:00
Cedricium
6572c90d49 'Create New Organization' added to Settings page
This fixes #281, which will allow users to open an external link in their default
browser to create a new organization on zulipchat.com.
2017-09-14 15:13:31 -07:00
akashnimare
1ed0011c88 Added crash reported fixes #295 2017-09-13 01:24:02 +05:30
Akash Nimare
6dd79b205c Merge pull request #293 from cedricium/shortcut-settings
Adding keyboard shortcuts to Settings page
2017-09-11 20:23:11 +05:30
Cedricium
538c18fa90 Shorten 'Keyboard Shortcuts' to just 'Shortcuts' 2017-09-11 07:39:19 -07:00
Cedricium
29e347c511 List application-specific shortcuts only 2017-09-10 14:07:04 -07:00
Cedricium
ad37a5e0a6 Changed 'Ctrl/Cmd' to appropriate user OS key
If Windows or Linux, variable `userOSKey` will be 'Ctrl'. For Macs, `userOSKey`
will be '⌘' and these values will show up in place of the previous 'Ctrl/Cmd' keys.
2017-09-10 00:32:06 -07:00
Cedricium
352b775e27 Added all keyboard shortcuts
Finished adding all keyboard shortcuts to the Settings page. Styled the tables
such that they are uniform with their columns being the same
width.

At the bottom of the 'Keyboard Shortcuts' settings page, a link to the
complete keyboard shortcuts documentation
(https://chat.zulip.org/help/keyboard-shortcuts) was also added.
2017-09-09 22:39:34 -07:00
Cedricium
38cec25680 Adding keyboard shortcuts in Settings
This is the initial pass at adding keyboard shortcuts to the Settings page. In
this commit, the `ShortcutsSection` class has been created and is applied to a
newly-added 'Keyboard Shortcuts' nav item. The template for ShortcutsSection
is essentially multiple settings cards containing one table of keyboard short-
cuts organized by their underlying functionality.

The HTML `<kbd>` tag was defined in preference.css which styles the element to
look like a keyboard key, similar to StackOverflow or GitHub.
2017-09-09 19:11:19 -07:00
Akash Nimare
f77ab92202 Merge pull request #287 from vbNETonIce/patch-1
Windows set up instructions
2017-09-08 16:51:20 +05:30
Akash Nimare
e843a29316 Update Windows.md 2017-09-08 16:50:59 +05:30
akashnimare
85837242e7 Add back tray icon on windows #289 2017-09-08 05:17:14 +05:30
akashnimare
9f6da5712e Add show/hide desktop notification setting #192 2017-09-08 04:27:15 +05:30
akashnimare
158685a869 code refactor for mouse events 2017-09-07 22:50:20 +05:30
Akash Nimare
288b1cb3f2 Merge pull request #290 from zulip/clear-settings
Added Clear app settings menu item
2017-09-07 22:36:43 +05:30
akashnimare
e24a966d48 Add shortcut for clearing app data 2017-09-07 03:34:30 +05:30
akashnimare
306feb2eff reset window position 2017-09-07 03:29:04 +05:30
akashnimare
f426c932b0 Relaunch app on clearing app data 2017-09-06 16:23:56 +05:30
Akash Nimare
26172d8508 Merge pull request #288 from vbNETonIce/patch-2
tiny change in wording of new reset data option
2017-09-06 16:07:17 +05:30
vbNETonIce
99da0a338f tiny change in wording of new reset data option 2017-09-06 12:36:20 +02:00
akashnimare
9599249b31 Add reset app settings menu item #286 2017-09-06 15:23:22 +05:30
vbNETonIce
4bdd2564b7 Windows set up instructions 2017-09-06 08:57:13 +02:00
Akash Nimare
4dcf22a53c Merge pull request #285 from zulip/reset-data-setting
Add reset app data setting #192
2017-09-06 04:08:37 +05:30
akashnimare
0dc97648a0 code refactor 2017-09-06 03:54:25 +05:30
akashnimare
787f097cf3 style reset data button 2017-09-06 03:42:39 +05:30
akashnimare
5481d55c66 remove clear cache menu items 2017-09-06 03:26:56 +05:30
akashnimare
2a052b2c38 Added reset app data functionality [WIP] 2017-09-06 03:15:23 +05:30
akashnimare
3ed253d2e1 🎉 v1.4.0 2017-09-04 20:12:58 +05:30
akashnimare
5f672fe404 updated dependencies 2017-09-04 20:04:02 +05:30
akashnimare
10372787ac updated dependencies 2017-08-31 01:52:23 +05:30
akashnimare
bcabb615b4 clear app data setting option [WIP] 2017-08-29 04:26:16 +05:30
Akash Nimare
68acf2ec64 Merge pull request #282 from zulip/auto-launch
Added auto launch Zulip on system startup setting option #192
2017-08-29 03:32:59 +05:30
akashnimare
afd24035f4 Don't run start at login script in dev mode 2017-08-29 03:28:07 +05:30
akashnimare
4f28f6b935 Add open at login setting [WIP] 2017-08-29 01:47:19 +05:30
akashnimare
52bd600690 Detailed certificate warning message #126 2017-08-28 23:50:15 +05:30
Akash Nimare
c7ce2a8a99 updated placeholder text
organization >> server
2017-08-28 20:19:36 +05:30
akashnimare
9760b1bf98 Add start app at login template 2017-08-28 17:11:27 +05:30
akashnimare
d579c267f0 code refactor 2017-08-28 16:34:33 +05:30
Akash Nimare
21f01d268a Merge pull request #280 from zulip/badge-setting
Add an option to show/hide badges and taskbar icons #192
2017-08-27 05:13:19 +05:30
akashnimare
33782e0492 disable text-selection in setting page 2017-08-27 05:00:18 +05:30
akashnimare
8d20568b7a Show badge on app load by default 2017-08-27 04:50:44 +05:30
akashnimare
06f38e92ce refactor code 2017-08-27 04:28:03 +05:30
akashnimare
855e96e40e refactoring code for badge/taskbar icon [WIP] 2017-08-27 03:58:08 +05:30
akashnimare
bb68720ab7 add badge-setting constructor [WIP] 2017-08-27 01:50:13 +05:30
akashnimare
044d007a0c Hide/Show badge setting option [WIP] 2017-08-26 05:28:59 +05:30
akashnimare
4ea95fe8e8 Relaunch app when new updated has been downloaded fixes #279 2017-08-26 03:18:06 +05:30
Akash Nimare
d17c685e4d Document windows portable installer fixes #148 2017-08-25 18:26:56 +05:30
Akash Nimare
bb174da59a Merge pull request #278 from geeeeeeeeek/feature/connect-through-proxy
Feature/connect through proxy
2017-08-25 18:15:36 +05:30
Akash Nimare
a028b80adb Add proxy rules examples 2017-08-25 18:08:39 +05:30
akashnimare
3683511e60 updated windows build config 2017-08-24 05:08:14 +05:30
Akash Nimare
bf359db7f4 Updated download instructions 2017-08-24 04:53:43 +05:30
Akash Nimare
949d786f1f installation instructions [WIP] 2017-08-24 04:49:25 +05:30
Zhongyi Tong
5cd0c1ca4e Do not show alert after turning off the proxy. 2017-08-21 11:10:01 -04:00
Zhongyi Tong
86674991c1 Fix incorrect base component. 2017-08-21 11:06:59 -04:00
Zhongyi Tong
8492cda092 Add placeholder for proxy inputs. 2017-08-20 23:04:40 -04:00
Zhongyi Tong
647303c001 Implement proxy. 2017-08-20 23:00:50 -04:00
Zhongyi Tong
fe34f8adad Finish setting section for proxy. 2017-08-20 21:59:31 -04:00
Zhongyi Tong
f142a2eb4e Move help.md outside app folder. 2017-08-20 16:59:48 -04:00
Zhongyi Tong
43eaa3dd63 Initialize user guide. 2017-08-20 16:58:31 -04:00
Zhongyi Tong
a3142713f7 Initialize setting section for proxy. 2017-08-20 16:35:39 -04:00
Zhongyi Tong
876936125a Refactor general setting section. 2017-08-20 16:00:10 -04:00
Zhongyi Tong
7261fb6cef Update settings layout. 2017-08-20 15:21:13 -04:00
akashnimare
fba52e6dff code refactoring 2017-08-20 03:51:31 +05:30
akashnimare
23ac347fb9 Additional app categories [Linux] 2017-08-19 20:52:14 +05:30
akashnimare
082aebb1e0 Add app category [Linux] fixes #277 2017-08-19 20:49:44 +05:30
akashnimare
3b18dc4df0 improve auto-updates logging
From now on, all the logs will be saved within the Zulip application directory.
2017-08-19 02:21:50 +05:30
akashnimare
39d30b92fa Fixing beta updates [WIP] #269 2017-08-18 20:47:33 +05:30
Akash Nimare
733209e04e Merge pull request #274 from geeeeeeeeek/feature/load-all-tabs-on-startup
Load all servers on app start-up.
2017-08-18 15:25:59 +05:30
Zhongyi Tong
a68d697fc5 Fix linting. 2017-08-17 14:16:12 -04:00
Zhongyi Tong
e52ece57df Load all servers on app start-up. 2017-08-17 14:14:23 -04:00
akashnimare
65681c7da9 Add pre-commit test 2017-08-17 16:40:20 +05:30
akashnimare
52b6209905 Allow spacing between curly braces [XO Linter] 2017-08-17 16:27:34 +05:30
akashnimare
0e37925418 Handle crash events 2017-08-17 16:23:18 +05:30
Akash Nimare
1a31d2e431 Merge pull request #273 from geeeeeeeeek/issue/new-server-icon-cache
Issue/new server icon cache
2017-08-16 23:52:58 +05:30
Zhongyi Tong
c2fab2c7bc Fix duplicate checking. 2017-08-16 13:22:59 -04:00
Zhongyi Tong
965b55ba5f Fix regression of avoiding duplicated servers. 2017-08-16 12:48:31 -04:00
akashnimare
2c7bb36f8e Submit new server form when Enter key is pressed 2017-08-16 04:48:41 +05:30
Zhongyi Tong
48b829b771 Lazy update server settings on reload. 2017-08-15 17:45:38 -04:00
Zhongyi Tong
f13c28db73 Generate stored icon path using url hash. 2017-08-15 17:42:40 -04:00
Akash Nimare
19157c77e4 Merge pull request #272 from Anshuman-Verma/es6-index.js
Use ES6 destructuring for grabbing elements
2017-08-15 00:10:51 +05:30
Akash Nimare
161cd80d38 fixed linting errors 2017-08-14 23:07:17 +05:30
Anshuman
65db4c8f39 Use ES6 destructuring for grabbing elements 2017-08-13 23:04:48 +05:30
akashnimare
d600d3a6ed clear autoupdate dialog message 2017-08-12 17:38:50 +05:30
akashnimare
5fdd971966 Check for duplicate domains 2017-08-11 03:26:34 +05:30
Akash Nimare
d77d39d6ad Merge pull request #270 from geeeeeeeeek/issue/refine-add-server
Issue/refine add server
2017-08-10 15:54:45 +05:30
Akash Nimare
bf35db26bd fixed linting errors 2017-08-10 15:44:25 +05:30
Zhongyi Tong
4d8c8e08a0 Shorten loading animation of functional tabs. 2017-08-10 13:04:07 +08:00
Zhongyi Tong
b34681ece8 Show new-server form by default. 2017-08-10 13:00:25 +08:00
231 changed files with 27232 additions and 3538 deletions

View File

@@ -1,15 +1,11 @@
root = true
[*]
indent_style = tab
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[{package.json,*.yml}]
[{*.css,*.html,*.js,*.json,*.ts}]
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = false

15
.gitattributes vendored
View File

@@ -1 +1,14 @@
* text=auto
* text=auto eol=lf
*.gif binary
*.jpg binary
*.jpeg binary
*.eot binary
*.woff binary
*.woff2 binary
*.svg binary
*.ttf binary
*.png binary
*.otf binary
*.tif binary
*.ogg binary

3
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,3 @@
github: zulip
patreon: zulip
open_collective: zulip

31
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,31 @@
---
name: Bug report
about: Create a report to help us improve
---
**Describe the bug**
<!-- A clear and concise description of what the bug is. -->
**To Reproduce**
<!-- Clear steps to reproduce the issue. -->
**Expected behavior**
<!-- A clear and concise description of what you expected to happen. -->
**Screenshots**
<!-- If applicable, add screenshots to help explain your problem. -->
**Desktop (please complete the following information):**
- Operating System:
<!-- (Platform and Version) e.g. macOS 10.13.6 / Windows 10 (1803) / Ubuntu 18.04 x64 -->
- Zulip Desktop Version:
<!-- e.g. 5.2.0 -->
**Additional context**
<!-- Add any other context about the problem here. -->

4
.github/ISSUE_TEMPLATE/custom.md vendored Normal file
View File

@@ -0,0 +1,4 @@
---
name: Custom issue template
about: Describe this issue template's purpose here.
---

View File

@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
---
**Problem Description**
<!-- Please add a clear and concise description of what the problem is. -->
**Proposed Solution**
<!-- Describe the solution you'd like in a clear and concise manner -->
**Describe alternatives you've considered**
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
**Additional context**
<!-- Add any other context or screenshots about the feature request here. -->

18
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,18 @@
---
<!--
Remove the fields that are not appropriate
Please include:
-->
**What's this PR do?**
**Any background context you want to provide?**
**Screenshots?**
**You have tested this PR on:**
- [ ] Windows
- [ ] Linux/Ubuntu
- [ ] macOS

15
.github/workflows/node.js.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
name: Node.js CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: npm ci
- run: npm test

24
.gitignore vendored
View File

@@ -1,11 +1,22 @@
# Dependency directories
node_modules/
# Dependency directory
/node_modules/
# npm cache directory
.npm
# transifexrc - if user prefers it to be in working tree
.transifexrc
# Compiled binary build directory
dist/
/dist/
/dist-electron/
#snap generated files
snap/parts
snap/prime
snap/snap
snap/stage
snap/*.snap
# Logs
logs
@@ -23,4 +34,9 @@ yarn-error.log*
# miscellaneous
.idea
config.gypi
config.gypi
# Test generated files
# tests/package.json
.python-version

19
.htmlhintrc Normal file
View File

@@ -0,0 +1,19 @@
{
"attr-value-not-empty": false,
"attr-no-duplication": true,
"doctype-first": true,
"spec-char-escape": true,
"id-unique": true,
"src-not-empty": true,
"title-require": true,
"alt-require": false,
"doctype-html5": true,
"id-class-value": "dash",
"style-disabled": false,
"inline-style-disabled": false,
"inline-script-disabled": false,
"id-class-ad-disabled": false,
"href-abs-or-rel": false,
"attr-unsafe-chars": true,
"head-script-disabled": true
}

5
.mailmap Normal file
View File

@@ -0,0 +1,5 @@
Anders Kaseorg <anders@zulip.com> <anders@zulipchat.com>
Rishi Gupta <rishig@zulip.com> <rishig@zulipchat.com>
Rishi Gupta <rishig@zulip.com> <rishig@users.noreply.github.com>
Tim Abbott <tabbott@zulip.com> <tabbott@zulipchat.com>
Tim Abbott <tabbott@zulip.com> <tabbott@mit.edu>

View File

@@ -1 +0,0 @@
6.9.4

3
.prettierignore Normal file
View File

@@ -0,0 +1,3 @@
/dist
/dist-electron
/public/translations/*.json

View File

@@ -1 +0,0 @@
2.7.9

12
.stylelintrc Normal file
View File

@@ -0,0 +1,12 @@
{
"extends": ["stylelint-config-standard"],
"rules": {
"color-named": "never",
"color-no-hex": true,
"font-family-no-missing-generic-family-keyword": [
true,
{"ignoreFontFamilies": ["Material Icons"]}
],
"selector-type-no-unknown": [true, {"ignoreTypes": ["webview"]}]
}
}

View File

@@ -1,36 +0,0 @@
sudo: required
dist: trusty
os:
- osx
- linux
addons:
apt:
packages:
- build-essential
- libxext-dev
- libxtst-dev
- libxkbfile-dev
language: node_js
node_js:
- '6'
before_install:
- npm install -g gulp
- npm install
cache:
directories:
- node_modules
- app/node_modules
script:
- npm run travis
notifications:
webhooks:
urls:
- https://zulip.org/zulipbot/travis
on_success: always
on_failure: always

9
.tx/config Normal file
View File

@@ -0,0 +1,9 @@
[main]
host = https://www.transifex.com
[o:zulip:p:zulip:r:desktopjson]
file_filter = public/translations/<lang>.json
minimum_perc = 0
source_file = public/translations/en.json
source_lang = en
type = KEYVALUEJSON

View File

@@ -2,46 +2,50 @@
Thanks for taking the time to contribute!
The following is a set of guidelines for contributing to Zulip Electron Desktop Client. These are just guidelines, not rules, so use your best judgement and feel free to propose changes to this document in a pull request.
The following is a set of guidelines for contributing to Zulip's desktop Client. These are just guidelines, not rules, so use your best judgement and feel free to propose changes to this document in a pull request.
## Getting Started
Zulip-Desktop app is built on top of [Electron](http://electron.atom.io/). If you are new to Electron, please head over to [this](http://jlord.us/essential-electron/) great article.
Zulip-Desktop app is built on top of [Electron](http://electron.atom.io/). If you are new to Electron, please head over to [this](https://jlord.us/essential-electron) great article.
## Community
* The whole Zulip documentation, such as setting up a development environment, setting up with the Zulip webapp project, and testing, can be read [here](https://zulip.readthedocs.io).
- The whole Zulip documentation, such as setting up a development environment, setting up with the Zulip web app project, and testing, can be read [here](https://zulip.readthedocs.io).
* If you have any questions regarding zulip-electron, open an [issue](https://github.com/zulip/zulip-electron/issues/new/) or ask it on [chat.zulip.org](https://chat.zulip.org/#narrow/stream/electron).
- If you have any questions regarding zulip-desktop, open an [issue](https://github.com/zulip/zulip-desktop/issues/new/) or ask it on [chat.zulip.org](https://chat.zulip.org/#narrow/stream/16-desktop).
## Issue
Ensure the bug was not already reported by searching on GitHub under [issues](https://github.com/zulip/zulip-electron/issues). If you're unable to find an open issue addressing the bug, open a [new issue](https://github.com/zulip/zulip-electron/issues/new).
Ensure the bug was not already reported by searching on GitHub under [issues](https://github.com/zulip/zulip-desktop/issues). If you're unable to find an open issue addressing the bug, open a [new issue](https://github.com/zulip/zulip-desktop/issues/new).
The [zulipbot](https://github.com/zulip/zulipbot) helps to claim an issue by commenting the following in the comment section: "**@zulipbot** claim". **@zulipbot** will assign you to the issue and label the issue as **in progress**. For more details, check out [**@zulipbot**](https://github.com/zulip/zulipbot).
Please pay attention to the following points while opening an issue.
### Does it happen on web browsers? (especially Chrome)
Zulip's desktop client is based on Electron, which integrates the Chrome engine within a standalone application.
If the problem you encounter can be reproduced on web browsers, it may be an issue with [Zulip web app](https://github.com/zulip/zulip).
### Write detailed information
Detailed information is very helpful to understand an issue.
For example:
* How to reproduce the issue, step-by-step.
* The expected behavior (or what is wrong).
* Screenshots for GUI issues.
* The application version.
* The operating system.
* The Zulip-Desktop version.
- How to reproduce the issue, step-by-step.
- The expected behavior (or what is wrong).
- Screenshots for GUI issues.
- The application version.
- The operating system.
- The Zulip-Desktop version.
## Pull Requests
Pull Requests are always welcome.
Pull Requests are always welcome.
1. When you edit the code, please run `npm run test` to check the formatting of your code before you `git commit`.
2. Ensure the PR description clearly describes the problem and solution. It should include:
* The operating system on which you tested.
* The Zulip-Desktop version on which you tested.
* The relevant issue number, if applicable.
- The operating system on which you tested.
- The Zulip-Desktop version on which you tested.
- The relevant issue number, if applicable.

View File

@@ -1,7 +0,0 @@
---
Please include:
- `Operating System`
- `Clear steps to reproduce the issue`
- `Relevant error messages and/or screenshots`

View File

@@ -1,39 +1,41 @@
# Zulip Desktop Client
[![Build Status](https://travis-ci.org/zulip/zulip-electron.svg?branch=master)](https://travis-ci.org/zulip/zulip-electron)
[![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/zulip/zulip-electron?branch=master&svg=true)](https://ci.appveyor.com/project/akashnimare/zulip-electron/branch/master)
[![Build Status](https://travis-ci.com/zulip/zulip-desktop.svg?branch=main)](https://travis-ci.com/github/zulip/zulip-desktop)
[![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/zulip/zulip-desktop?branch=main&svg=true)](https://ci.appveyor.com/project/zulip/zulip-desktop/branch/main)
[![XO code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo)
[![project chat](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://chat.zulip.org)
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"/>
![screenshot](https://i.imgur.com/s1o6TRA.png)
![screenshot](https://i.imgur.com/vekKnW4.png)
# Download
You can download the latest version from the [Releases](https://github.com/zulip/zulip-electron/releases/latest) page.
Please see the [installation guide](https://zulip.com/help/desktop-app-install-guide).
# Features
* Sign in to multiple teams
* Native desktop Notifications
* SpellChecker
* OSX/Win/Linux installers
* Automatic Updates (macOS/Windows)
* Keyboard shortcuts
Description | Keys
-----------------------| -----------------------
Default shortcuts | <kbd>Cmd/Ctrl</kbd> <kbd>k</kbd>
Manage Zulip Servers | <kbd>Cmd/Ctrl</kbd> <kbd>,</kbd>
Back | <kbd>Cmd/Ctrl</kbd> <kbd>[</kbd>
Forward | <kbd>Cmd/Ctrl</kbd> <kbd>]</kbd>
- Sign in to multiple organizations
- Desktop notifications with inline reply
- Tray/dock integration
- Multi-language spell checker
- Automatic updates
# Development
Please see our [development guide](./development.md) to get started and run app locally.
# Reporting issues
This desktop client shares most of its code with the Zulip web app.
Issues in an individual organization's Zulip window should be reported
in the [Zulip server and web app
project](https://github.com/zulip/zulip/issues/new). Other
issues in the desktop app and its settings should be reported [in this
project](https://github.com/zulip/zulip-desktop/issues/new).
# Contribute
If you want to contribute please make sure to read [our documentation about contributing](./CONTRIBUTING.md) first.
* [Issue Tracker](https://github.com/zulip/zulip-electron/issues)
* [Source Code](https://github.com/zulip/zulip-electron/)
First, join us on the [Zulip community server](https://zulip.readthedocs.io/en/latest/contributing/chat-zulip-org.html)!
Also see our [contribution guidelines](./CONTRIBUTING.md) and our [development guide](./development.md).
# License
Released under the [Apache-2.0](./LICENSE) license.

View File

@@ -0,0 +1,44 @@
import {z} from "zod";
export const dndSettingsSchemata = {
showNotification: z.boolean(),
silent: z.boolean(),
flashTaskbarOnMessage: z.boolean(),
};
export const configSchemata = {
...dndSettingsSchemata,
appLanguage: z.string().nullable(),
autoHideMenubar: z.boolean(),
autoUpdate: z.boolean(),
badgeOption: z.boolean(),
betaUpdate: z.boolean(),
// eslint-disable-next-line @typescript-eslint/naming-convention
customCSS: z.string().or(z.literal(false)).nullable(),
dnd: z.boolean(),
dndPreviousSettings: z.object(dndSettingsSchemata).partial(),
dockBouncing: z.boolean(),
downloadsPath: z.string(),
enableSpellchecker: z.boolean(),
errorReporting: z.boolean(),
lastActiveTab: z.number(),
promptDownload: z.boolean(),
proxyBypass: z.string(),
// eslint-disable-next-line @typescript-eslint/naming-convention
proxyPAC: z.string(),
proxyRules: z.string(),
quitOnClose: z.boolean(),
showSidebar: z.boolean(),
spellcheckerLanguages: z.string().array().nullable(),
startAtLogin: z.boolean(),
startMinimized: z.boolean(),
trayIcon: z.boolean(),
useManualProxy: z.boolean(),
useProxy: z.boolean(),
useSystemProxy: z.boolean(),
};
export const enterpriseConfigSchemata = {
...configSchemata,
presetOrganizations: z.string().array(),
};

100
app/common/config-util.ts Normal file
View File

@@ -0,0 +1,100 @@
import fs from "node:fs";
import path from "node:path";
import * as Sentry from "@sentry/electron";
import {JsonDB} from "node-json-db";
import {DataError} from "node-json-db/dist/lib/Errors";
import type {z} from "zod";
import {app, dialog} from "zulip:remote";
import {configSchemata} from "./config-schemata.js";
import * as EnterpriseUtil from "./enterprise-util.js";
import Logger from "./logger-util.js";
export type Config = {
[Key in keyof typeof configSchemata]: z.output<(typeof configSchemata)[Key]>;
};
const logger = new Logger({
file: "config-util.log",
});
let db: JsonDB;
reloadDb();
export function getConfigItem<Key extends keyof Config>(
key: Key,
defaultValue: Config[Key],
): z.output<(typeof configSchemata)[Key]> {
try {
db.reload();
} catch (error: unknown) {
logger.error("Error while reloading settings.json: ");
logger.error(error);
}
try {
return configSchemata[key].parse(db.getObject<unknown>(`/${key}`));
} catch (error: unknown) {
if (!(error instanceof DataError)) throw error;
setConfigItem(key, defaultValue);
return defaultValue;
}
}
// This function returns whether a key exists in the configuration file (settings.json)
export function isConfigItemExists(key: string): boolean {
try {
db.reload();
} catch (error: unknown) {
logger.error("Error while reloading settings.json: ");
logger.error(error);
}
return db.exists(`/${key}`);
}
export function setConfigItem<Key extends keyof Config>(
key: Key,
value: Config[Key],
override?: boolean,
): void {
if (EnterpriseUtil.configItemExists(key) && !override) {
// If item is in global config and we're not trying to override
return;
}
configSchemata[key].parse(value);
db.push(`/${key}`, value, true);
db.save();
}
export function removeConfigItem(key: string): void {
db.delete(`/${key}`);
db.save();
}
function reloadDb(): void {
const settingsJsonPath = path.join(
app.getPath("userData"),
"/config/settings.json",
);
try {
const file = fs.readFileSync(settingsJsonPath, "utf8");
JSON.parse(file);
} catch (error: unknown) {
if (fs.existsSync(settingsJsonPath)) {
fs.unlinkSync(settingsJsonPath);
dialog.showErrorBox(
"Error saving settings",
"We encountered an error while saving the settings.",
);
logger.error("Error while JSON parsing settings.json: ");
logger.error(error);
Sentry.captureException(error);
}
}
db = new JsonDB(settingsJsonPath, true, true);
}

View File

@@ -0,0 +1,61 @@
import fs from "node:fs";
import {app} from "zulip:remote";
let setupCompleted = false;
const zulipDir = app.getPath("userData");
const logDir = `${zulipDir}/Logs/`;
const configDir = `${zulipDir}/config/`;
export const initSetUp = (): void => {
// If it is the first time the app is running
// create zulip dir in userData folder to
// avoid errors
if (!setupCompleted) {
if (!fs.existsSync(zulipDir)) {
fs.mkdirSync(zulipDir);
}
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir);
}
// Migrate config files from app data folder to config folder inside app
// data folder. This will be done once when a user updates to the new version.
if (!fs.existsSync(configDir)) {
fs.mkdirSync(configDir);
const domainJson = `${zulipDir}/domain.json`;
const settingsJson = `${zulipDir}/settings.json`;
const updatesJson = `${zulipDir}/updates.json`;
const windowStateJson = `${zulipDir}/window-state.json`;
const configData = [
{
path: domainJson,
fileName: "domain.json",
},
{
path: settingsJson,
fileName: "settings.json",
},
{
path: updatesJson,
fileName: "updates.json",
},
];
for (const data of configData) {
if (fs.existsSync(data.path)) {
fs.copyFileSync(data.path, configDir + data.fileName);
fs.unlinkSync(data.path);
}
}
// `window-state.json` is only deleted not moved, as the electron-window-state
// package will recreate the file in the config folder.
if (fs.existsSync(windowStateJson)) {
fs.unlinkSync(windowStateJson);
}
}
setupCompleted = true;
}
};

60
app/common/dnd-util.ts Normal file
View File

@@ -0,0 +1,60 @@
import process from "node:process";
import type {z} from "zod";
import type {dndSettingsSchemata} from "./config-schemata.js";
import * as ConfigUtil from "./config-util.js";
export type DndSettings = {
[Key in keyof typeof dndSettingsSchemata]: z.output<
(typeof dndSettingsSchemata)[Key]
>;
};
type SettingName = keyof DndSettings;
type Toggle = {
dnd: boolean;
newSettings: Partial<DndSettings>;
};
export function toggle(): Toggle {
const dnd = !ConfigUtil.getConfigItem("dnd", false);
const dndSettingList: SettingName[] = ["showNotification", "silent"];
if (process.platform === "win32") {
dndSettingList.push("flashTaskbarOnMessage");
}
let newSettings: Partial<DndSettings>;
if (dnd) {
const oldSettings: Partial<DndSettings> = {};
newSettings = {};
// Iterate through the dndSettingList.
for (const settingName of dndSettingList) {
// Store the current value of setting.
oldSettings[settingName] = ConfigUtil.getConfigItem(
settingName,
settingName !== "silent",
);
// New value of setting.
newSettings[settingName] = settingName === "silent";
}
// Store old value in oldSettings.
ConfigUtil.setConfigItem("dndPreviousSettings", oldSettings);
} else {
newSettings = ConfigUtil.getConfigItem("dndPreviousSettings", {
showNotification: true,
silent: false,
...(process.platform === "win32" && {flashTaskbarOnMessage: true}),
});
}
for (const settingName of dndSettingList) {
ConfigUtil.setConfigItem(settingName, newSettings[settingName]!);
}
ConfigUtil.setConfigItem("dnd", dnd);
return {dnd, newSettings};
}

View File

@@ -0,0 +1,94 @@
import fs from "node:fs";
import path from "node:path";
import process from "node:process";
import {z} from "zod";
import {enterpriseConfigSchemata} from "./config-schemata.js";
import Logger from "./logger-util.js";
type EnterpriseConfig = {
[Key in keyof typeof enterpriseConfigSchemata]: z.output<
(typeof enterpriseConfigSchemata)[Key]
>;
};
const logger = new Logger({
file: "enterprise-util.log",
});
let enterpriseSettings: Partial<EnterpriseConfig>;
let configFile: boolean;
reloadDb();
function reloadDb(): void {
let enterpriseFile = "/etc/zulip-desktop-config/global_config.json";
if (process.platform === "win32") {
enterpriseFile =
"C:\\Program Files\\Zulip-Desktop-Config\\global_config.json";
}
enterpriseFile = path.resolve(enterpriseFile);
if (fs.existsSync(enterpriseFile)) {
configFile = true;
try {
const file = fs.readFileSync(enterpriseFile, "utf8");
const data: unknown = JSON.parse(file);
enterpriseSettings = z
.object(enterpriseConfigSchemata)
.partial()
.parse(data);
} catch (error: unknown) {
logger.log("Error while JSON parsing global_config.json: ");
logger.log(error);
}
} else {
configFile = false;
}
}
export function hasConfigFile(): boolean {
return configFile;
}
export function getConfigItem<Key extends keyof EnterpriseConfig>(
key: Key,
defaultValue: EnterpriseConfig[Key],
): EnterpriseConfig[Key] {
reloadDb();
if (!configFile) {
return defaultValue;
}
const value = enterpriseSettings[key];
return value === undefined ? defaultValue : (value as EnterpriseConfig[Key]);
}
export function configItemExists(key: keyof EnterpriseConfig): boolean {
reloadDb();
if (!configFile) {
return false;
}
return enterpriseSettings[key] !== undefined;
}
export function isPresetOrg(url: string): boolean {
if (!configFile || !configItemExists("presetOrganizations")) {
return false;
}
const presetOrgs = enterpriseSettings.presetOrganizations;
if (!Array.isArray(presetOrgs)) {
throw new TypeError("Expected array for presetOrgs");
}
for (const org of presetOrgs) {
if (url.includes(org)) {
return true;
}
}
return false;
}

26
app/common/html.ts Normal file
View File

@@ -0,0 +1,26 @@
import {htmlEscape} from "escape-goat";
export class Html {
html: string;
constructor({html}: {html: string}) {
this.html = html;
}
join(htmls: readonly Html[]): Html {
return new Html({html: htmls.map((html) => html.html).join(this.html)});
}
}
export function html(
template: TemplateStringsArray,
...values: unknown[]
): Html {
let html = template[0];
for (const [index, value] of values.entries()) {
html += value instanceof Html ? value.html : htmlEscape(String(value));
html += template[index + 1];
}
return new Html({html});
}

43
app/common/link-util.ts Normal file
View File

@@ -0,0 +1,43 @@
import {shell} from "electron/common";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import {html} from "./html.js";
export async function openBrowser(url: URL): Promise<void> {
if (["http:", "https:", "mailto:"].includes(url.protocol)) {
await shell.openExternal(url.href);
} else {
// For security, indirect links to non-whitelisted protocols
// through a real web browser via a local HTML file.
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "zulip-redirect-"));
const file = path.join(dir, "redirect.html");
fs.writeFileSync(
file,
html`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="Refresh" content="0; url=${url.href}" />
<title>Redirecting</title>
<style>
html {
font-family: menu, "Helvetica Neue", sans-serif;
}
</style>
</head>
<body>
<p>Opening <a href="${url.href}">${url.href}</a>…</p>
</body>
</html>
`.html,
);
await shell.openPath(file);
setTimeout(() => {
fs.unlinkSync(file);
fs.rmdirSync(dir);
}, 15_000);
}
}

90
app/common/logger-util.ts Normal file
View File

@@ -0,0 +1,90 @@
import {Console} from "node:console"; // eslint-disable-line n/prefer-global/console
import fs from "node:fs";
import os from "node:os";
import process from "node:process";
import {app} from "zulip:remote";
import {initSetUp} from "./default-util.js";
type LoggerOptions = {
file?: string;
};
initSetUp();
const logDir = `${app.getPath("userData")}/Logs`;
type Level = "log" | "debug" | "info" | "warn" | "error";
export default class Logger {
nodeConsole: Console;
constructor(options: LoggerOptions = {}) {
let {file = "console.log"} = options;
file = `${logDir}/${file}`;
// Trim log according to type of process
if (process.type === "renderer") {
requestIdleCallback(async () => this.trimLog(file));
} else {
process.nextTick(async () => this.trimLog(file));
}
const fileStream = fs.createWriteStream(file, {flags: "a"});
const nodeConsole = new Console(fileStream);
this.nodeConsole = nodeConsole;
}
_log(type: Level, ...args: unknown[]): void {
args.unshift(this.getTimestamp() + " |\t");
args.unshift(type.toUpperCase() + " |");
this.nodeConsole[type](...args);
console[type](...args);
}
log(...args: unknown[]): void {
this._log("log", ...args);
}
debug(...args: unknown[]): void {
this._log("debug", ...args);
}
info(...args: unknown[]): void {
this._log("info", ...args);
}
warn(...args: unknown[]): void {
this._log("warn", ...args);
}
error(...args: unknown[]): void {
this._log("error", ...args);
}
getTimestamp(): string {
const date = new Date();
const timestamp =
`${date.getMonth()}/${date.getDate()} ` +
`${date.getMinutes()}:${date.getSeconds()}`;
return timestamp;
}
async trimLog(file: string): Promise<void> {
const data = await fs.promises.readFile(file, "utf8");
const maxLogFileLines = 500;
const logs = data.split(os.EOL);
const logLength = logs.length - 1;
// Keep bottom maxLogFileLines of each log instance
if (logLength > maxLogFileLines) {
const trimmedLogs = logs.slice(logLength - maxLogFileLines);
const toWrite = trimmedLogs.join(os.EOL);
await fs.promises.writeFile(file, toWrite);
}
}
}

38
app/common/messages.ts Normal file
View File

@@ -0,0 +1,38 @@
type DialogBoxError = {
title: string;
content: string;
};
export function invalidZulipServerError(domain: string): string {
return `${domain} does not appear to be a valid Zulip server. Make sure that
• You can connect to that URL in a web browser.
• If you need a proxy to connect to the Internet, that you've configured your proxy in the Network settings.
• It's a Zulip server. (The oldest supported version is 1.6).
• The server has a valid certificate.
• The SSL is correctly configured for the certificate. Check out the SSL troubleshooting guide -
https://zulip.readthedocs.io/en/stable/production/ssl-certificates.html`;
}
export function enterpriseOrgError(
length: number,
domains: string[],
): DialogBoxError {
let domainList = "";
for (const domain of domains) {
domainList += `${domain}\n`;
}
return {
title: `Could not add the following ${
length === 1 ? "organization" : "organizations"
}`,
content: `${domainList}\nPlease contact your system administrator.`,
};
}
export function orgRemovalError(url: string): DialogBoxError {
return {
title: `Removing ${url} is a restricted operation.`,
content: "Please contact your system administrator.",
};
}

15
app/common/paths.ts Normal file
View File

@@ -0,0 +1,15 @@
import path from "node:path";
import process from "node:process";
import url from "node:url";
export const bundlePath = __dirname;
export const publicPath = import.meta.env.DEV
? path.join(bundlePath, "../public")
: bundlePath;
export const bundleUrl = import.meta.env.DEV
? process.env.VITE_DEV_SERVER_URL
: url.pathToFileURL(__dirname).href + "/";
export const publicUrl = bundleUrl;

View File

@@ -0,0 +1,16 @@
import path from "node:path";
import i18n from "i18n";
import * as ConfigUtil from "./config-util.js";
import {publicPath} from "./paths.js";
i18n.configure({
directory: path.join(publicPath, "translations/"),
updateFiles: false,
});
/* Fetches the current appLocale from settings.json */
i18n.setLocale(ConfigUtil.getConfigItem("appLanguage", "en") ?? "en");
export {__} from "i18n";

84
app/common/typed-ipc.ts Normal file
View File

@@ -0,0 +1,84 @@
import type {DndSettings} from "./dnd-util.js";
import type {MenuProps, ServerConf} from "./types.js";
export type MainMessage = {
"clear-app-settings": () => void;
"configure-spell-checker": () => void;
"fetch-user-agent": () => string;
"focus-app": () => void;
"focus-this-webview": () => void;
"new-clipboard-key": () => {key: Uint8Array; sig: Uint8Array};
"permission-callback": (permissionCallbackId: number, grant: boolean) => void;
"quit-app": () => void;
"realm-icon-changed": (serverURL: string, iconURL: string) => void;
"realm-name-changed": (serverURL: string, realmName: string) => void;
"reload-full-app": () => void;
"save-last-tab": (index: number) => void;
"switch-server-tab": (index: number) => void;
"toggle-app": () => void;
"toggle-badge-option": (newValue: boolean) => void;
"toggle-menubar": (showMenubar: boolean) => void;
toggleAutoLauncher: (AutoLaunchValue: boolean) => void;
"unread-count": (unreadCount: number) => void;
"update-badge": (messageCount: number) => void;
"update-menu": (props: MenuProps) => void;
"update-taskbar-icon": (data: string, text: string) => void;
};
export type MainCall = {
"get-server-settings": (domain: string) => ServerConf;
"is-online": (url: string) => boolean;
"poll-clipboard": (key: Uint8Array, sig: Uint8Array) => string | undefined;
"save-server-icon": (iconURL: string) => string | null;
};
export type RendererMessage = {
back: () => void;
"copy-zulip-url": () => void;
destroytray: () => void;
"enter-fullscreen": () => void;
focus: () => void;
"focus-webview-with-id": (webviewId: number) => void;
forward: () => void;
"hard-reload": () => void;
"leave-fullscreen": () => void;
"log-out": () => void;
logout: () => void;
"new-server": () => void;
"open-about": () => void;
"open-help": () => void;
"open-network-settings": () => void;
"open-org-tab": () => void;
"open-settings": () => void;
"permission-request": (
options: {webContentsId: number | null; origin: string; permission: string},
rendererCallbackId: number,
) => void;
"play-ding-sound": () => void;
"reload-current-viewer": () => void;
"reload-proxy": (showAlert: boolean) => void;
"reload-viewer": () => void;
"render-taskbar-icon": (messageCount: number) => void;
"set-active": () => void;
"set-idle": () => void;
"show-keyboard-shortcuts": () => void;
"show-notification-settings": () => void;
"switch-server-tab": (index: number) => void;
"tab-devtools": () => void;
"toggle-autohide-menubar": (
autoHideMenubar: boolean,
updateMenu: boolean,
) => void;
"toggle-dnd": (state: boolean, newSettings: Partial<DndSettings>) => void;
"toggle-sidebar": (show: boolean) => void;
"toggle-silent": (state: boolean) => void;
"toggle-tray": (state: boolean) => void;
toggletray: () => void;
tray: (arg: number) => void;
"update-realm-icon": (serverURL: string, iconURL: string) => void;
"update-realm-name": (serverURL: string, realmName: string) => void;
"webview-reload": () => void;
zoomActualSize: () => void;
zoomIn: () => void;
zoomOut: () => void;
};

28
app/common/types.ts Normal file
View File

@@ -0,0 +1,28 @@
export type MenuProps = {
tabs: TabData[];
activeTabIndex?: number;
enableMenu?: boolean;
};
export type NavItem =
| "General"
| "Network"
| "AddServer"
| "Organizations"
| "Shortcuts";
export type ServerConf = {
url: string;
alias: string;
icon: string;
zulipVersion: string;
zulipFeatureLevel: number;
};
export type TabRole = "server" | "function";
export type TabData = {
role: TabRole;
name: string;
index: number;
};

View File

@@ -1,37 +0,0 @@
'use strict';
const {app, dialog} = require('electron');
const {autoUpdater} = require('electron-updater');
const ConfigUtil = require('./../renderer/js/utils/config-util.js');
function appUpdater() {
// Log whats happening
const log = require('electron-log');
log.transports.file.level = 'info';
autoUpdater.logger = log;
// Handle auto updates for beta/pre releases
autoUpdater.allowPrerelease = ConfigUtil.getConfigItem('betaUpdate') || false;
// Ask the user if update is available
// eslint-disable-next-line no-unused-vars
autoUpdater.on('update-downloaded', (event, info) => {
// Ask user to update the app
dialog.showMessageBox({
type: 'question',
buttons: ['Install and Relaunch', 'Later'],
defaultId: 0,
message: 'A new version of ' + app.getName() + ' has been downloaded',
detail: 'It will be installed the next time you restart the application'
}, response => {
if (response === 0) {
setTimeout(() => autoUpdater.quitAndInstall(), 1);
}
});
});
// Init for updates
autoUpdater.checkForUpdates();
}
module.exports = {
appUpdater
};

119
app/main/autoupdater.ts Normal file
View File

@@ -0,0 +1,119 @@
import {shell} from "electron/common";
import {app, dialog, session} from "electron/main";
import process from "node:process";
import log from "electron-log";
import type {UpdateDownloadedEvent, UpdateInfo} from "electron-updater";
import {autoUpdater} from "electron-updater";
import * as ConfigUtil from "../common/config-util.js";
import {linuxUpdateNotification} from "./linuxupdater.js"; // Required only in case of linux
let quitting = false;
export function shouldQuitForUpdate(): boolean {
return quitting;
}
export async function appUpdater(updateFromMenu = false): Promise<void> {
// Don't initiate auto-updates in development
if (!app.isPackaged) {
return;
}
if (process.platform === "linux" && !process.env.APPIMAGE) {
const ses = session.fromPartition("persist:webviewsession");
await linuxUpdateNotification(ses);
return;
}
let updateAvailable = false;
// Log what's happening
log.transports.file.fileName = "updates.log";
log.transports.file.level = "info";
autoUpdater.logger = log;
// Handle auto updates for beta/pre releases
const isBetaUpdate = ConfigUtil.getConfigItem("betaUpdate", false);
autoUpdater.allowPrerelease = isBetaUpdate;
const eventsListenerRemove = [
"update-available",
"update-not-available",
] as const;
autoUpdater.on("update-available", async (info: UpdateInfo) => {
if (updateFromMenu) {
updateAvailable = true;
// This is to prevent removal of 'update-downloaded' and 'error' event listener.
for (const event of eventsListenerRemove) {
autoUpdater.removeAllListeners(event);
}
await dialog.showMessageBox({
message: `A new version ${info.version}, of Zulip Desktop is available`,
detail:
"The update will be downloaded in the background. You will be notified when it is ready to be installed.",
});
}
});
autoUpdater.on("update-not-available", async () => {
if (updateFromMenu) {
// Remove all autoUpdator listeners so that next time autoUpdator is manually called these
// listeners don't trigger multiple times.
autoUpdater.removeAllListeners();
await dialog.showMessageBox({
message: "No updates available",
detail: `You are running the latest version of Zulip Desktop.\nVersion: ${app.getVersion()}`,
});
}
});
autoUpdater.on("error", async (error: Error) => {
if (updateFromMenu) {
// Remove all autoUpdator listeners so that next time autoUpdator is manually called these
// listeners don't trigger multiple times.
autoUpdater.removeAllListeners();
const messageText = updateAvailable
? "Unable to download the updates"
: "Unable to check for updates";
const {response} = await dialog.showMessageBox({
type: "error",
buttons: ["Manual Download", "Cancel"],
message: messageText,
detail: `Error: ${error.message}
The latest version of Zulip Desktop is available at -
https://zulip.com/apps/.
Current Version: ${app.getVersion()}`,
});
if (response === 0) {
await shell.openExternal("https://zulip.com/apps/");
}
}
});
// Ask the user if update is available
autoUpdater.on("update-downloaded", async (event: UpdateDownloadedEvent) => {
// Ask user to update the app
const {response} = await dialog.showMessageBox({
type: "question",
buttons: ["Install and Relaunch", "Install Later"],
defaultId: 0,
message: `A new update ${event.version} has been downloaded`,
detail: "It will be installed the next time you restart the application",
});
if (response === 0) {
quitting = true;
autoUpdater.quitAndInstall();
}
});
// Init for updates
await autoUpdater.checkForUpdates();
}

View File

@@ -0,0 +1,61 @@
import {nativeImage} from "electron/common";
import type {BrowserWindow} from "electron/main";
import {app} from "electron/main";
import process from "node:process";
import * as ConfigUtil from "../common/config-util.js";
import {send} from "./typed-ipc-main.js";
function showBadgeCount(messageCount: number, mainWindow: BrowserWindow): void {
if (process.platform === "win32") {
updateOverlayIcon(messageCount, mainWindow);
} else {
app.badgeCount = messageCount;
}
}
function hideBadgeCount(mainWindow: BrowserWindow): void {
if (process.platform === "win32") {
mainWindow.setOverlayIcon(null, "");
} else {
app.badgeCount = 0;
}
}
export function updateBadge(
badgeCount: number,
mainWindow: BrowserWindow,
): void {
if (ConfigUtil.getConfigItem("badgeOption", true)) {
showBadgeCount(badgeCount, mainWindow);
} else {
hideBadgeCount(mainWindow);
}
}
function updateOverlayIcon(
messageCount: number,
mainWindow: BrowserWindow,
): void {
if (!mainWindow.isFocused()) {
mainWindow.flashFrame(
ConfigUtil.getConfigItem("flashTaskbarOnMessage", true),
);
}
if (messageCount === 0) {
mainWindow.setOverlayIcon(null, "");
} else {
send(mainWindow.webContents, "render-taskbar-icon", messageCount);
}
}
export function updateTaskbarIcon(
data: string,
text: string,
mainWindow: BrowserWindow,
): void {
const img = nativeImage.createFromDataURL(data);
mainWindow.setOverlayIcon(img, text);
}

View File

@@ -0,0 +1,163 @@
import {shell} from "electron/common";
import type {
HandlerDetails,
SaveDialogOptions,
WebContents,
} from "electron/main";
import {Notification, app} from "electron/main";
import fs from "node:fs";
import path from "node:path";
import * as ConfigUtil from "../common/config-util.js";
import * as LinkUtil from "../common/link-util.js";
import {send} from "./typed-ipc-main.js";
function isUploadsUrl(server: string, url: URL): boolean {
return url.origin === server && url.pathname.startsWith("/user_uploads/");
}
function downloadFile({
contents,
url,
downloadPath,
completed,
failed,
}: {
contents: WebContents;
url: string;
downloadPath: string;
completed(filePath: string, fileName: string): Promise<void>;
failed(state: string): void;
}) {
contents.downloadURL(url);
contents.session.once("will-download", async (_event: Event, item) => {
if (ConfigUtil.getConfigItem("promptDownload", false)) {
const showDialogOptions: SaveDialogOptions = {
defaultPath: path.join(downloadPath, item.getFilename()),
};
item.setSaveDialogOptions(showDialogOptions);
} else {
const getTimeStamp = (): number => {
const date = new Date();
return date.getTime();
};
const formatFile = (filePath: string): string => {
const fileExtension = path.extname(filePath);
const baseName = path.basename(filePath, fileExtension);
return `${baseName}-${getTimeStamp()}${fileExtension}`;
};
const filePath = path.join(downloadPath, item.getFilename());
// Update the name and path of the file if it already exists
const updatedFilePath = path.join(downloadPath, formatFile(filePath));
const setFilePath: string = fs.existsSync(filePath)
? updatedFilePath
: filePath;
item.setSavePath(setFilePath);
}
const updatedListener = (_event: Event, state: string): void => {
switch (state) {
case "interrupted": {
// Can interrupted to due to network error, cancel download then
console.log(
"Download interrupted, cancelling and fallback to dialog download.",
);
item.cancel();
break;
}
case "progressing": {
if (item.isPaused()) {
item.cancel();
}
// This event can also be used to show progress in percentage in future.
break;
}
default: {
console.info("Unknown updated state of download item");
}
}
};
item.on("updated", updatedListener);
item.once("done", async (_event: Event, state) => {
if (state === "completed") {
await completed(item.getSavePath(), path.basename(item.getSavePath()));
} else {
console.log("Download failed state:", state);
failed(state);
}
// To stop item for listening to updated events of this file
item.removeListener("updated", updatedListener);
});
});
}
export default function handleExternalLink(
contents: WebContents,
details: HandlerDetails,
mainContents: WebContents,
): void {
let url: URL;
try {
url = new URL(details.url);
} catch {
return;
}
const downloadPath = ConfigUtil.getConfigItem(
"downloadsPath",
`${app.getPath("downloads")}`,
);
if (isUploadsUrl(new URL(contents.getURL()).origin, url)) {
downloadFile({
contents,
url: url.href,
downloadPath,
async completed(filePath: string, fileName: string) {
const downloadNotification = new Notification({
title: "Download Complete",
body: `Click to show ${fileName} in folder`,
silent: true, // We'll play our own sound - ding.ogg
});
downloadNotification.on("click", () => {
// Reveal file in download folder
shell.showItemInFolder(filePath);
});
downloadNotification.show();
// Play sound to indicate download complete
if (!ConfigUtil.getConfigItem("silent", false)) {
send(mainContents, "play-ding-sound");
}
},
failed(state: string) {
// Automatic download failed, so show save dialog prompt and download
// through webview
// Only do this if it is the automatic download, otherwise show an error (so we aren't showing two save
// prompts right after each other)
// Check that the download is not cancelled by user
if (state !== "cancelled") {
if (ConfigUtil.getConfigItem("promptDownload", false)) {
new Notification({
title: "Download Complete",
body: "Download failed",
}).show();
} else {
contents.downloadURL(url.href);
}
}
},
});
} else {
(async () => LinkUtil.openBrowser(url))();
}
}

View File

@@ -1,256 +0,0 @@
'use strict';
const path = require('path');
const electron = require('electron');
const {app} = require('electron');
const ipc = require('electron').ipcMain;
const electronLocalshortcut = require('electron-localshortcut');
const isDev = require('electron-is-dev');
const windowStateKeeper = require('electron-window-state');
const appMenu = require('./menu');
const {appUpdater} = require('./autoupdater');
// Adds debug features like hotkeys for triggering dev tools and reload
require('electron-debug')();
// Prevent window being garbage collected
let mainWindow;
let isQuitting = false;
// Load this url in main window
const mainURL = 'file://' + path.join(__dirname, '../renderer', 'main.html');
const isAlreadyRunning = app.makeSingleInstance(() => {
if (mainWindow) {
if (mainWindow.isMinimized()) {
mainWindow.restore();
}
mainWindow.show();
}
});
if (isAlreadyRunning) {
return app.quit();
}
function isWindowsOrmacOS() {
return process.platform === 'darwin' || process.platform === 'win32';
}
const APP_ICON = path.join(__dirname, '../resources', 'Icon');
const iconPath = () => {
return APP_ICON + (process.platform === 'win32' ? '.ico' : '.png');
};
function createMainWindow() {
// Load the previous state with fallback to defaults
const mainWindowState = windowStateKeeper({
defaultWidth: 1000,
defaultHeight: 600
});
const win = new electron.BrowserWindow({
// This settings needs to be saved in config
title: 'Zulip',
icon: iconPath(),
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,
height: mainWindowState.height,
minWidth: 600,
minHeight: 500,
webPreferences: {
plugins: true,
allowDisplayingInsecureContent: true,
nodeIntegration: true
},
show: false
});
win.on('focus', () => {
win.webContents.send('focus');
});
win.once('ready-to-show', () => {
win.show();
});
win.loadURL(mainURL);
// Keep the app running in background on close event
win.on('close', e => {
if (!isQuitting) {
e.preventDefault();
if (process.platform === 'darwin') {
app.hide();
} else {
win.hide();
}
}
// Unregister all the shortcuts so that they don't interfare with other apps
electronLocalshortcut.unregisterAll(mainWindow);
});
win.setTitle('Zulip');
win.on('enter-full-screen', () => {
win.webContents.send('enter-fullscreen');
});
win.on('leave-full-screen', () => {
win.webContents.send('leave-fullscreen');
});
// To destroy tray icon when navigate to a new URL
win.webContents.on('will-navigate', e => {
if (e) {
win.webContents.send('destroytray');
}
});
// Let us register listeners on the window, so we can update the state
// automatically (the listeners will be removed when the window is closed)
// and restore the maximized or full screen state
mainWindowState.manage(win);
return win;
}
function registerLocalShortcuts(page) {
// Somehow, reload action cannot be overwritten by the menu item
electronLocalshortcut.register(mainWindow, 'CommandOrControl+R', () => {
page.send('reload-viewer');
});
// Also adding these shortcuts because some users might want to use it instead of CMD/Left-Right
electronLocalshortcut.register(mainWindow, 'CommandOrControl+[', () => {
page.send('back');
});
electronLocalshortcut.register(mainWindow, 'CommandOrControl+]', () => {
page.send('forward');
});
}
// eslint-disable-next-line max-params
app.on('certificate-error', (event, webContents, url, error, certificate, callback) => {
event.preventDefault();
callback(true);
});
app.on('window-all-closed', () => {
// Unregister all the shortcuts so that they don't interfare with other apps
electronLocalshortcut.unregisterAll(mainWindow);
});
app.on('activate', () => {
if (!mainWindow) {
mainWindow = createMainWindow();
}
});
app.on('ready', () => {
appMenu.setMenu({
tabs: []
});
mainWindow = createMainWindow();
const page = mainWindow.webContents;
registerLocalShortcuts(page);
page.on('dom-ready', () => {
mainWindow.show();
});
page.once('did-frame-finish-load', () => {
const checkOS = isWindowsOrmacOS();
if (checkOS && !isDev) {
// Initate auto-updates on MacOS and Windows
appUpdater();
}
});
electron.powerMonitor.on('resume', () => {
page.send('reload-viewer');
});
ipc.on('focus-app', () => {
mainWindow.show();
});
ipc.on('quit-app', () => {
app.quit();
});
// Reload full app not just webview, useful in debugging
ipc.on('reload-full-app', () => {
mainWindow.reload();
page.send('destroytray');
});
ipc.on('toggle-app', () => {
if (mainWindow.isVisible()) {
mainWindow.hide();
} else {
mainWindow.show();
}
});
ipc.on('update-badge', (event, messageCount) => {
if (process.platform === 'darwin') {
app.setBadgeCount(messageCount);
}
if (process.platform === 'win32') {
if (!mainWindow.isFocused()) {
mainWindow.flashFrame(true);
}
if (messageCount === 0) {
mainWindow.setOverlayIcon(null, '');
} else {
page.send('render-taskbar-icon', messageCount);
}
}
page.send('tray', messageCount);
});
ipc.on('update-taskbar-icon', (event, data, text) => {
const img = electron.nativeImage.createFromDataURL(data);
mainWindow.setOverlayIcon(img, text);
});
ipc.on('forward-message', (event, listener, ...params) => {
page.send(listener, ...params);
});
ipc.on('update-menu', (event, props) => {
appMenu.setMenu(props);
});
ipc.on('register-server-tab-shortcut', (event, index) => {
electronLocalshortcut.register(mainWindow, `CommandOrControl+${index}`, () => {
// Array index == Shown index - 1
page.send('switch-server-tab', index - 1);
});
});
ipc.on('local-shortcuts', (event, enable) => {
if (enable) {
registerLocalShortcuts(page);
} else {
electronLocalshortcut.unregisterAll(mainWindow);
}
});
});
app.on('will-quit', () => {
// Unregister all the shortcuts so that they don't interfare with other apps
electronLocalshortcut.unregisterAll(mainWindow);
});
app.on('before-quit', () => {
isQuitting = true;
});

454
app/main/index.ts Normal file
View File

@@ -0,0 +1,454 @@
import {clipboard} from "electron/common";
import type {IpcMainEvent, WebContents} from "electron/main";
import {BrowserWindow, app, dialog, powerMonitor, session} from "electron/main";
import {Buffer} from "node:buffer";
import crypto from "node:crypto";
import path from "node:path";
import process from "node:process";
import * as remoteMain from "@electron/remote/main";
import windowStateKeeper from "electron-window-state";
import * as ConfigUtil from "../common/config-util.js";
import {bundlePath, bundleUrl, publicPath} from "../common/paths.js";
import type {RendererMessage} from "../common/typed-ipc.js";
import type {MenuProps} from "../common/types.js";
import {appUpdater, shouldQuitForUpdate} from "./autoupdater.js";
import * as BadgeSettings from "./badge-settings.js";
import handleExternalLink from "./handle-external-link.js";
import * as AppMenu from "./menu.js";
import {_getServerSettings, _isOnline, _saveServerIcon} from "./request.js";
import {sentryInit} from "./sentry.js";
import {setAutoLaunch} from "./startup.js";
import {ipcMain, send} from "./typed-ipc-main.js";
import "gatemaker/electron-setup"; // eslint-disable-line import/no-unassigned-import
// eslint-disable-next-line @typescript-eslint/naming-convention
const {GDK_BACKEND} = process.env;
// Initialize sentry for main process
sentryInit();
let mainWindowState: windowStateKeeper.State;
// Prevent window being garbage collected
let mainWindow: BrowserWindow;
let badgeCount: number;
let isQuitting = false;
// Load this file in main window
const mainUrl = new URL("app/renderer/main.html", bundleUrl).href;
const permissionCallbacks = new Map<number, (grant: boolean) => void>();
let nextPermissionCallbackId = 0;
const appIcon = path.join(publicPath, "resources/Icon");
const iconPath = (): string =>
appIcon + (process.platform === "win32" ? ".ico" : ".png");
// Toggle the app window
const toggleApp = (): void => {
if (!mainWindow.isVisible() || mainWindow.isMinimized()) {
mainWindow.show();
} else {
mainWindow.hide();
}
};
function createMainWindow(): BrowserWindow {
// Load the previous state with fallback to defaults
mainWindowState = windowStateKeeper({
defaultWidth: 1100,
defaultHeight: 720,
path: `${app.getPath("userData")}/config`,
});
const win = new BrowserWindow({
// This settings needs to be saved in config
title: "Zulip",
icon: iconPath(),
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,
height: mainWindowState.height,
minWidth: 500,
minHeight: 400,
webPreferences: {
preload: path.join(bundlePath, "renderer.js"),
sandbox: false,
webviewTag: true,
},
show: false,
});
remoteMain.enable(win.webContents);
win.on("focus", () => {
send(win.webContents, "focus");
});
(async () => win.loadURL(mainUrl))();
// Keep the app running in background on close event
win.on("close", (event) => {
if (ConfigUtil.getConfigItem("quitOnClose", false)) {
app.quit();
}
if (!isQuitting && !shouldQuitForUpdate()) {
event.preventDefault();
if (process.platform === "darwin") {
app.hide();
} else {
win.hide();
}
}
});
win.setTitle("Zulip");
win.on("enter-full-screen", () => {
send(win.webContents, "enter-fullscreen");
});
win.on("leave-full-screen", () => {
send(win.webContents, "leave-fullscreen");
});
// To destroy tray icon when navigate to a new URL
win.webContents.on("will-navigate", (event) => {
if (event) {
send(win.webContents, "destroytray");
}
});
// Let us register listeners on the window, so we can update the state
// automatically (the listeners will be removed when the window is closed)
// and restore the maximized or full screen state
mainWindowState.manage(win);
return win;
}
(async () => {
if (!app.requestSingleInstanceLock()) {
app.quit();
return;
}
await app.whenReady();
if (process.env.GDK_BACKEND !== GDK_BACKEND) {
console.warn(
"Reverting GDK_BACKEND to work around https://github.com/electron/electron/issues/28436",
);
if (GDK_BACKEND === undefined) {
delete process.env.GDK_BACKEND;
} else {
process.env.GDK_BACKEND = GDK_BACKEND;
}
}
// Used for notifications on Windows
app.setAppUserModelId("org.zulip.zulip-electron");
remoteMain.initialize();
app.on("second-instance", () => {
if (mainWindow) {
if (mainWindow.isMinimized()) {
mainWindow.restore();
}
mainWindow.show();
}
});
ipcMain.on(
"permission-callback",
(event: Event, permissionCallbackId: number, grant: boolean) => {
permissionCallbacks.get(permissionCallbackId)?.(grant);
permissionCallbacks.delete(permissionCallbackId);
},
);
// This event is only available on macOS. Triggers when you click on the dock icon.
app.on("activate", () => {
mainWindow.show();
});
app.on("web-contents-created", (_event: Event, contents: WebContents) => {
contents.setWindowOpenHandler((details) => {
handleExternalLink(contents, details, page);
return {action: "deny"};
});
});
const ses = session.fromPartition("persist:webviewsession");
ses.setUserAgent(`ZulipElectron/${app.getVersion()} ${ses.getUserAgent()}`);
function configureSpellChecker() {
const enable = ConfigUtil.getConfigItem("enableSpellchecker", true);
if (enable && process.platform !== "darwin") {
ses.setSpellCheckerLanguages(
ConfigUtil.getConfigItem("spellcheckerLanguages", null) ?? [],
);
}
ses.setSpellCheckerEnabled(enable);
}
configureSpellChecker();
ipcMain.on("configure-spell-checker", configureSpellChecker);
const clipboardSigKey = crypto.randomBytes(32);
ipcMain.on("new-clipboard-key", (event) => {
const key = crypto.randomBytes(32);
const hmac = crypto.createHmac("sha256", clipboardSigKey);
hmac.update(key);
event.returnValue = {key, sig: hmac.digest()};
});
ipcMain.handle("poll-clipboard", (event, key, sig) => {
// Check that the key was generated here.
const hmac = crypto.createHmac("sha256", clipboardSigKey);
hmac.update(key);
if (!crypto.timingSafeEqual(sig, hmac.digest())) return;
try {
// Check that the data on the clipboard was encrypted to the key.
const data = Buffer.from(clipboard.readText(), "hex");
const iv = data.slice(0, 12);
const ciphertext = data.slice(12, -16);
const authTag = data.slice(-16);
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv, {
authTagLength: 16,
});
decipher.setAuthTag(authTag);
return (
decipher.update(ciphertext, undefined, "utf8") + decipher.final("utf8")
);
} catch {
// If the parsing or decryption failed in any way,
// the correct token hasnt been copied yet; try
// again next time.
return undefined;
}
});
AppMenu.setMenu({
tabs: [],
});
mainWindow = createMainWindow();
// Auto-hide menu bar on Windows + Linux
if (process.platform !== "darwin") {
const shouldHideMenu = ConfigUtil.getConfigItem("autoHideMenubar", false);
mainWindow.autoHideMenuBar = shouldHideMenu;
mainWindow.setMenuBarVisibility(!shouldHideMenu);
}
const page = mainWindow.webContents;
page.on("dom-ready", () => {
if (ConfigUtil.getConfigItem("startMinimized", false)) {
mainWindow.hide();
} else {
mainWindow.show();
}
});
ipcMain.on("fetch-user-agent", (event) => {
event.returnValue = session
.fromPartition("persist:webviewsession")
.getUserAgent();
});
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", true)) {
await appUpdater();
}
});
app.on(
"certificate-error",
(
event: Event,
webContents: WebContents,
urlString: string,
error: string,
) => {
const url = new URL(urlString);
dialog.showErrorBox(
"Certificate error",
`The server presented an invalid certificate for ${url.origin}:
${error}`,
);
},
);
ses.setPermissionRequestHandler(
(webContents, permission, callback, details) => {
const {origin} = new URL(details.requestingUrl);
const permissionCallbackId = nextPermissionCallbackId++;
permissionCallbacks.set(permissionCallbackId, callback);
send(
page,
"permission-request",
{
webContentsId:
webContents.id === mainWindow.webContents.id
? null
: webContents.id,
origin,
permission,
},
permissionCallbackId,
);
},
);
// Temporarily remove this event
// powerMonitor.on('resume', () => {
// mainWindow.reload();
// send(page, 'destroytray');
// });
ipcMain.on("focus-app", () => {
mainWindow.show();
});
ipcMain.on("quit-app", () => {
app.quit();
});
// Reload full app not just webview, useful in debugging
ipcMain.on("reload-full-app", () => {
mainWindow.reload();
send(page, "destroytray");
});
ipcMain.on("clear-app-settings", () => {
mainWindowState.unmanage();
app.relaunch();
app.exit();
});
ipcMain.on("toggle-app", () => {
toggleApp();
});
ipcMain.on("toggle-badge-option", () => {
BadgeSettings.updateBadge(badgeCount, mainWindow);
});
ipcMain.on("toggle-menubar", (_event: IpcMainEvent, showMenubar: boolean) => {
mainWindow.autoHideMenuBar = showMenubar;
mainWindow.setMenuBarVisibility(!showMenubar);
send(page, "toggle-autohide-menubar", showMenubar, true);
});
ipcMain.on("update-badge", (_event: IpcMainEvent, messageCount: number) => {
badgeCount = messageCount;
BadgeSettings.updateBadge(badgeCount, mainWindow);
send(page, "tray", messageCount);
});
ipcMain.on(
"update-taskbar-icon",
(_event: IpcMainEvent, data: string, text: string) => {
BadgeSettings.updateTaskbarIcon(data, text, mainWindow);
},
);
ipcMain.on(
"forward-message",
<Channel extends keyof RendererMessage>(
_event: IpcMainEvent,
listener: Channel,
...parameters: Parameters<RendererMessage[Channel]>
) => {
send(page, listener, ...parameters);
},
);
ipcMain.on("update-menu", (_event: IpcMainEvent, props: MenuProps) => {
AppMenu.setMenu(props);
if (props.activeTabIndex !== undefined) {
const activeTab = props.tabs[props.activeTabIndex];
mainWindow.setTitle(`Zulip - ${activeTab.name}`);
}
});
ipcMain.on(
"toggleAutoLauncher",
async (_event: IpcMainEvent, AutoLaunchValue: boolean) => {
await setAutoLaunch(AutoLaunchValue);
},
);
ipcMain.on(
"realm-name-changed",
(_event: IpcMainEvent, serverURL: string, realmName: string) => {
send(page, "update-realm-name", serverURL, realmName);
},
);
ipcMain.on(
"realm-icon-changed",
(_event: IpcMainEvent, serverURL: string, iconURL: string) => {
send(page, "update-realm-icon", serverURL, iconURL);
},
);
ipcMain.on("save-last-tab", (_event: IpcMainEvent, index: number) => {
ConfigUtil.setConfigItem("lastActiveTab", index);
});
ipcMain.on("focus-this-webview", (event: IpcMainEvent) => {
send(page, "focus-webview-with-id", event.sender.id);
mainWindow.show();
});
// Update user idle status for each realm after every 15s
const idleCheckInterval = 15 * 1000; // 15 seconds
setInterval(() => {
// Set user idle if no activity in 1 second (idleThresholdSeconds)
const idleThresholdSeconds = 1; // 1 second
const idleState = powerMonitor.getSystemIdleState(idleThresholdSeconds);
if (idleState === "active") {
send(page, "set-active");
} else {
send(page, "set-idle");
}
}, idleCheckInterval);
})();
app.on("before-quit", () => {
isQuitting = true;
});
// Send crash reports
process.on("uncaughtException", (error) => {
console.error(error);
console.error(error.stack);
});

View File

@@ -0,0 +1,69 @@
import {app, dialog} from "electron/main";
import fs from "node:fs";
import path from "node:path";
import {JsonDB} from "node-json-db";
import {DataError} from "node-json-db/dist/lib/Errors";
import Logger from "../common/logger-util.js";
const logger = new Logger({
file: "linux-update-util.log",
});
let db: JsonDB;
reloadDb();
export function getUpdateItem(
key: string,
defaultValue: true | null = null,
): true | null {
reloadDb();
let value: unknown;
try {
value = db.getObject<unknown>(`/${key}`);
} catch (error: unknown) {
if (!(error instanceof DataError)) throw error;
}
if (value !== true && value !== null) {
setUpdateItem(key, defaultValue);
return defaultValue;
}
return value;
}
export function setUpdateItem(key: string, value: true | null): void {
db.push(`/${key}`, value, true);
reloadDb();
}
export function removeUpdateItem(key: string): void {
db.delete(`/${key}`);
reloadDb();
}
function reloadDb(): void {
const linuxUpdateJsonPath = path.join(
app.getPath("userData"),
"/config/updates.json",
);
try {
const file = fs.readFileSync(linuxUpdateJsonPath, "utf8");
JSON.parse(file);
} catch (error: unknown) {
if (fs.existsSync(linuxUpdateJsonPath)) {
fs.unlinkSync(linuxUpdateJsonPath);
dialog.showErrorBox(
"Error saving update notifications.",
"We encountered an error while saving the update notifications.",
);
logger.error("Error while JSON parsing updates.json: ");
logger.error(error);
}
}
db = new JsonDB(linuxUpdateJsonPath, true, true);
}

50
app/main/linuxupdater.ts Normal file
View File

@@ -0,0 +1,50 @@
import type {Session} from "electron/main";
import {Notification, app, net} from "electron/main";
import getStream from "get-stream";
import * as semver from "semver";
import {z} from "zod";
import * as ConfigUtil from "../common/config-util.js";
import Logger from "../common/logger-util.js";
import * as LinuxUpdateUtil from "./linux-update-util.js";
import {fetchResponse} from "./request.js";
const logger = new Logger({
file: "linux-update-util.log",
});
export async function linuxUpdateNotification(session: Session): Promise<void> {
let url = "https://api.github.com/repos/zulip/zulip-desktop/releases";
url = ConfigUtil.getConfigItem("betaUpdate", false) ? url : url + "/latest";
try {
const response = await fetchResponse(net.request({url, session}));
if (response.statusCode !== 200) {
logger.log("Linux update response status: ", response.statusCode);
return;
}
const data: unknown = JSON.parse(await getStream(response));
/* eslint-disable @typescript-eslint/naming-convention */
const latestVersion = ConfigUtil.getConfigItem("betaUpdate", false)
? z.array(z.object({tag_name: z.string()})).parse(data)[0].tag_name
: z.object({tag_name: z.string()}).parse(data).tag_name;
/* eslint-enable @typescript-eslint/naming-convention */
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: unknown) {
logger.error("Linux update error.");
logger.error(error);
}
}

View File

@@ -1,381 +0,0 @@
'use strict';
const os = require('os');
const {dialog, app, shell, BrowserWindow, Menu} = require('electron');
const ConfigUtil = require(__dirname + '/../renderer/js/utils/config-util.js');
const appName = app.getName();
class AppMenu {
getHistorySubmenu() {
return [{
label: 'Back',
accelerator: process.platform === 'darwin' ? 'Command+Left' : 'Alt+Left',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('back');
}
}
}, {
label: 'Forward',
accelerator: process.platform === 'darwin' ? 'Command+Right' : 'Alt+Right',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('forward');
}
}
}];
}
getViewSubmenu() {
return [{
label: 'Reload',
accelerator: 'CommandOrControl+R',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('reload-viewer');
}
}
}, {
label: 'Hard Reload',
accelerator: 'CommandOrControl+Shift+R',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('hard-reload');
}
}
}, {
type: 'separator'
}, {
role: 'togglefullscreen'
}, {
label: 'Zoom In',
accelerator: 'CommandOrControl+=',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('zoomIn');
}
}
}, {
label: 'Zoom Out',
accelerator: 'CommandOrControl+-',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('zoomOut');
}
}
}, {
label: 'Actual Size',
accelerator: 'CommandOrControl+0',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('zoomActualSize');
}
}
}, {
type: 'separator'
}, {
label: 'Toggle Tray Icon',
click(item, focusedWindow) {
if (focusedWindow) {
focusedWindow.webContents.send('toggletray');
}
}
}, {
label: 'Toggle Sidebar',
accelerator: 'CommandOrControl+S',
click(item, focusedWindow) {
if (focusedWindow) {
const newValue = !ConfigUtil.getConfigItem('show-sidebar');
focusedWindow.webContents.send('toggle-sidebar', newValue);
ConfigUtil.setConfigItem('show-sidebar', newValue);
}
}
}, {
label: 'Toggle DevTools for Zulip App',
accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
click(item, focusedWindow) {
if (focusedWindow) {
focusedWindow.webContents.toggleDevTools();
}
}
}, {
label: 'Toggle DevTools for Active Tab',
accelerator: process.platform === 'darwin' ? 'Alt+Command+U' : 'Ctrl+Shift+U',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('tab-devtools');
}
}
}];
}
getHelpSubmenu() {
return [{
label: `${appName} Website`,
click() {
shell.openExternal('https://zulipchat.com/help/');
}
}, {
label: `${appName + 'Desktop'} - ${app.getVersion()}`,
enabled: false
}, {
label: 'Report an Issue...',
click() {
const body = `
<!-- Please succinctly describe your issue and steps to reproduce it. -->
-
${app.getName()} ${app.getVersion()}
Electron ${process.versions.electron}
${process.platform} ${process.arch} ${os.release()}`;
shell.openExternal(`https://github.com/zulip/zulip-electron/issues/new?body=${encodeURIComponent(body)}`);
}
}];
}
getWindowSubmenu(tabs, activeTabIndex) {
const initialSubmenu = [{
role: 'minimize'
}, {
role: 'close'
}];
if (tabs.length > 0) {
const ShortcutKey = process.platform === 'darwin' ? 'Cmd' : 'Ctrl';
initialSubmenu.push({
type: 'separator'
});
for (let i = 0; i < tabs.length; i++) {
initialSubmenu.push({
label: tabs[i].webview.props.name,
accelerator: tabs[i].props.role === 'function' ? '' : `${ShortcutKey} + ${tabs[i].props.index + 1}`,
checked: tabs[i].props.index === activeTabIndex,
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('switch-server-tab', tabs[i].props.index);
}
},
type: 'radio'
});
}
}
return initialSubmenu;
}
getDarwinTpl(props) {
const {tabs, activeTabIndex} = props;
return [{
label: `${app.getName()}`,
submenu: [{
label: 'Zulip Desktop',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('open-about');
}
}
}, {
type: 'separator'
}, {
label: 'Settings',
accelerator: 'Cmd+,',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('open-settings');
}
}
}, {
label: 'Keyboard Shortcuts',
accelerator: 'Cmd+K',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('shortcut');
}
}
}, {
type: 'separator'
}, {
label: 'Clear Cache',
click() {
AppMenu.clearCache();
}
}, {
label: 'Log Out',
accelerator: 'Cmd+L',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('log-out');
}
}
}, {
type: 'separator'
}, {
role: 'services',
submenu: []
}, {
type: 'separator'
}, {
role: 'hide'
}, {
role: 'hideothers'
}, {
role: 'unhide'
}, {
type: 'separator'
}, {
role: 'quit'
}]
}, {
label: 'Edit',
submenu: [{
role: 'undo'
}, {
role: 'redo'
}, {
type: 'separator'
}, {
role: 'cut'
}, {
role: 'copy'
}, {
role: 'paste'
}, {
role: 'pasteandmatchstyle'
}, {
role: 'delete'
}, {
role: 'selectall'
}]
}, {
label: 'View',
submenu: this.getViewSubmenu()
}, {
label: 'History',
submenu: this.getHistorySubmenu()
}, {
label: 'Window',
submenu: this.getWindowSubmenu(tabs, activeTabIndex)
}, {
role: 'help',
submenu: this.getHelpSubmenu()
}];
}
getOtherTpl(props) {
const {tabs, activeTabIndex} = props;
return [{
label: 'File',
submenu: [{
label: 'Zulip Desktop',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('open-about');
}
}
}, {
type: 'separator'
}, {
label: 'Settings',
accelerator: 'Ctrl+,',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('open-settings');
}
}
}, {
type: 'separator'
}, {
label: 'Keyboard Shortcuts',
accelerator: 'Ctrl+K',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('shortcut');
}
}
}, {
type: 'separator'
}, {
label: 'Clear Cache',
click() {
AppMenu.clearCache();
}
}, {
label: 'Log Out',
accelerator: 'Ctrl+L',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('log-out');
}
}
}, {
type: 'separator'
}, {
role: 'quit',
accelerator: 'Ctrl+Q'
}]
}, {
label: 'Edit',
submenu: [{
role: 'undo'
}, {
role: 'redo'
}, {
type: 'separator'
}, {
role: 'cut'
}, {
role: 'copy'
}, {
role: 'paste'
}, {
role: 'pasteandmatchstyle'
}, {
role: 'delete'
}, {
type: 'separator'
}, {
role: 'selectall'
}]
}, {
label: 'View',
submenu: this.getViewSubmenu()
}, {
label: 'History',
submenu: this.getHistorySubmenu()
}, {
label: 'Window',
submenu: this.getWindowSubmenu(tabs, activeTabIndex)
}, {
role: 'help',
submenu: this.getHelpSubmenu()
}];
}
static sendAction(action, ...params) {
const win = BrowserWindow.getAllWindows()[0];
if (process.platform === 'darwin') {
win.restore();
}
win.webContents.send(action, ...params);
}
static clearCache() {
const win = BrowserWindow.getAllWindows()[0];
const ses = win.webContents.session;
ses.clearCache(() => {
dialog.showMessageBox({type: 'info', buttons: [], message: 'Cache cleared!'});
});
}
setMenu(props) {
const tpl = process.platform === 'darwin' ? this.getDarwinTpl(props) : this.getOtherTpl(props);
const menu = Menu.buildFromTemplate(tpl);
Menu.setApplicationMenu(menu);
}
}
module.exports = new AppMenu();

722
app/main/menu.ts Normal file
View File

@@ -0,0 +1,722 @@
import {shell} from "electron/common";
import type {MenuItemConstructorOptions} from "electron/main";
import {BrowserWindow, Menu, app} from "electron/main";
import process from "node:process";
import AdmZip from "adm-zip";
import * as ConfigUtil from "../common/config-util.js";
import * as DNDUtil from "../common/dnd-util.js";
import * as t from "../common/translation-util.js";
import type {RendererMessage} from "../common/typed-ipc.js";
import type {MenuProps, TabData} from "../common/types.js";
import {appUpdater} from "./autoupdater.js";
import {send} from "./typed-ipc-main.js";
const appName = app.name;
function getHistorySubmenu(enableMenu: boolean): MenuItemConstructorOptions[] {
return [
{
label: t.__("Back"),
accelerator: process.platform === "darwin" ? "Command+Left" : "Alt+Left",
enabled: enableMenu,
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("back");
}
},
},
{
label: t.__("Forward"),
accelerator:
process.platform === "darwin" ? "Command+Right" : "Alt+Right",
enabled: enableMenu,
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("forward");
}
},
},
];
}
function getToolsSubmenu(): MenuItemConstructorOptions[] {
return [
{
label: t.__("Check for Updates"),
async click() {
await checkForUpdate();
},
},
{
label: t.__("Release Notes"),
async click() {
await shell.openExternal(
`https://github.com/zulip/zulip-desktop/releases/tag/v${app.getVersion()}`,
);
},
},
{
type: "separator",
},
{
label: t.__("Download App Logs"),
click() {
const zip = new AdmZip();
const date = new Date();
const dateString = date.toLocaleDateString().replace(/\//g, "-");
// Create a zip file of all the logs and config data
zip.addLocalFolder(`${app.getPath("appData")}/${appName}/Logs`);
zip.addLocalFolder(`${app.getPath("appData")}/${appName}/config`);
// Put the log file in downloads folder
const logFilePath = `${app.getPath(
"downloads",
)}/Zulip-logs-${dateString}.zip`;
zip.writeZip(logFilePath);
// Open and select the log file
shell.showItemInFolder(logFilePath);
},
},
{
type: "separator",
},
{
label: t.__("Toggle DevTools for Zulip App"),
accelerator:
process.platform === "darwin" ? "Alt+Command+I" : "Ctrl+Shift+I",
click(_item, focusedWindow) {
if (focusedWindow) {
focusedWindow.webContents.openDevTools({mode: "undocked"});
}
},
},
{
label: t.__("Toggle DevTools for Active Tab"),
accelerator:
process.platform === "darwin" ? "Alt+Command+U" : "Ctrl+Shift+U",
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("tab-devtools");
}
},
},
];
}
function getViewSubmenu(): MenuItemConstructorOptions[] {
return [
{
label: t.__("Reload"),
accelerator: "CommandOrControl+R",
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("reload-current-viewer");
}
},
},
{
label: t.__("Hard Reload"),
accelerator: "CommandOrControl+Shift+R",
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("hard-reload");
}
},
},
{
label: t.__("Hard Reload"),
visible: false,
accelerator: "F5",
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("hard-reload");
}
},
},
{
type: "separator",
},
{
label: t.__("Toggle Full Screen"),
role: "togglefullscreen",
},
{
label: t.__("Zoom In"),
accelerator: "CommandOrControl+=",
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("zoomIn");
}
},
},
{
label: t.__("Zoom In"),
visible: false,
accelerator: "CommandOrControl+Plus",
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("zoomIn");
}
},
},
{
label: t.__("Zoom In"),
visible: false,
accelerator: "CommandOrControl+numadd",
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("zoomIn");
}
},
},
{
label: t.__("Zoom Out"),
accelerator: "CommandOrControl+-",
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("zoomOut");
}
},
},
{
label: t.__("Zoom Out"),
visible: false,
accelerator: "CommandOrControl+numsub",
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("zoomOut");
}
},
},
{
label: t.__("Actual Size"),
accelerator: "CommandOrControl+0",
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("zoomActualSize");
}
},
},
{
label: t.__("Actual Size"),
visible: false,
accelerator: "CommandOrControl+num0",
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("zoomActualSize");
}
},
},
{
type: "separator",
},
{
label: t.__("Toggle Tray Icon"),
click(_item, focusedWindow) {
if (focusedWindow) {
send(focusedWindow.webContents, "toggletray");
}
},
},
{
label: t.__("Toggle Sidebar"),
accelerator: "CommandOrControl+Shift+S",
click(_item, focusedWindow) {
if (focusedWindow) {
const newValue = !ConfigUtil.getConfigItem("showSidebar", true);
send(focusedWindow.webContents, "toggle-sidebar", newValue);
ConfigUtil.setConfigItem("showSidebar", newValue);
}
},
},
{
label: t.__("Auto hide Menu bar"),
checked: ConfigUtil.getConfigItem("autoHideMenubar", false),
visible: process.platform !== "darwin",
click(_item, focusedWindow) {
if (focusedWindow) {
const newValue = !ConfigUtil.getConfigItem("autoHideMenubar", false);
focusedWindow.autoHideMenuBar = newValue;
focusedWindow.setMenuBarVisibility(!newValue);
send(
focusedWindow.webContents,
"toggle-autohide-menubar",
newValue,
false,
);
ConfigUtil.setConfigItem("autoHideMenubar", newValue);
}
},
type: "checkbox",
},
];
}
function getHelpSubmenu(): MenuItemConstructorOptions[] {
return [
{
label: `${appName + " Desktop"} v${app.getVersion()}`,
enabled: false,
},
{
label: t.__("About Zulip"),
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("open-about");
}
},
},
{
label: t.__("Help Center"),
click(focusedWindow) {
if (focusedWindow) {
sendAction("open-help");
}
},
},
{
label: t.__("Report an Issue"),
async click() {
await shell.openExternal("https://zulip.com/help/contact-support");
},
},
];
}
function getWindowSubmenu(
tabs: TabData[],
activeTabIndex?: number,
): MenuItemConstructorOptions[] {
const initialSubmenu: MenuItemConstructorOptions[] = [
{
label: t.__("Minimize"),
role: "minimize",
},
{
label: t.__("Close"),
role: "close",
},
];
if (tabs.length > 0) {
const shortcutKey = process.platform === "darwin" ? "Cmd" : "Ctrl";
initialSubmenu.push({
type: "separator",
});
for (const tab of tabs) {
// Skip missing elements left by `delete this.tabs[index]` in
// ServerManagerView.
if (tab === undefined) continue;
// Do not add functional tab settings to list of windows in menu bar
if (tab.role === "function" && tab.name === "Settings") {
continue;
}
initialSubmenu.push({
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.index);
}
},
type: "checkbox",
});
}
initialSubmenu.push(
{
type: "separator",
},
{
label: t.__("Switch to Next Organization"),
accelerator: "Ctrl+Tab",
enabled: tabs.length > 1,
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction(
"switch-server-tab",
getNextServer(tabs, activeTabIndex!),
);
}
},
},
{
label: t.__("Switch to Previous Organization"),
accelerator: "Ctrl+Shift+Tab",
enabled: tabs.length > 1,
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction(
"switch-server-tab",
getPreviousServer(tabs, activeTabIndex!),
);
}
},
},
);
}
return initialSubmenu;
}
function getDarwinTpl(props: MenuProps): MenuItemConstructorOptions[] {
const {tabs, activeTabIndex, enableMenu = false} = props;
return [
{
label: app.name,
submenu: [
{
label: t.__("Add Organization"),
accelerator: "Cmd+Shift+N",
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("new-server");
}
},
},
{
label: t.__("Toggle Do Not Disturb"),
accelerator: "Cmd+Shift+M",
click() {
const dndUtil = DNDUtil.toggle();
sendAction("toggle-dnd", dndUtil.dnd, dndUtil.newSettings);
},
},
{
label: t.__("Desktop Settings"),
accelerator: "Cmd+,",
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("open-settings");
}
},
},
{
label: t.__("Keyboard Shortcuts"),
accelerator: "Cmd+Shift+K",
enabled: enableMenu,
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("show-keyboard-shortcuts");
}
},
},
{
type: "separator",
},
{
label: t.__("Copy Zulip URL"),
accelerator: "Cmd+Shift+C",
enabled: enableMenu,
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("copy-zulip-url");
}
},
},
{
label: t.__("Log Out of Organization"),
enabled: enableMenu,
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("log-out");
}
},
},
{
type: "separator",
},
{
label: t.__("Services"),
role: "services",
submenu: [],
},
{
type: "separator",
},
{
label: t.__("Hide"),
role: "hide",
},
{
label: t.__("Hide Others"),
role: "hideOthers",
},
{
label: t.__("Unhide"),
role: "unhide",
},
{
type: "separator",
},
{
label: t.__("Minimize"),
role: "minimize",
},
{
label: t.__("Close"),
role: "close",
},
{
label: t.__("Quit"),
role: "quit",
},
],
},
{
label: t.__("Edit"),
submenu: [
{
label: t.__("Undo"),
role: "undo",
},
{
label: t.__("Redo"),
role: "redo",
},
{
type: "separator",
},
{
label: t.__("Cut"),
role: "cut",
},
{
label: t.__("Copy"),
role: "copy",
},
{
label: t.__("Paste"),
role: "paste",
},
{
label: t.__("Paste and Match Style"),
role: "pasteAndMatchStyle",
},
{
label: t.__("Select All"),
role: "selectAll",
},
],
},
{
label: t.__("View"),
submenu: getViewSubmenu(),
},
{
label: t.__("History"),
submenu: getHistorySubmenu(enableMenu),
},
{
label: t.__("Window"),
submenu: getWindowSubmenu(tabs, activeTabIndex),
},
{
label: t.__("Tools"),
submenu: getToolsSubmenu(),
},
{
label: t.__("Help"),
role: "help",
submenu: getHelpSubmenu(),
},
];
}
function getOtherTpl(props: MenuProps): MenuItemConstructorOptions[] {
const {tabs, activeTabIndex, enableMenu = false} = props;
return [
{
label: t.__("File"),
submenu: [
{
label: t.__("Add Organization"),
accelerator: "Ctrl+Shift+N",
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("new-server");
}
},
},
{
type: "separator",
},
{
label: t.__("Toggle Do Not Disturb"),
accelerator: "Ctrl+Shift+M",
click() {
const dndUtil = DNDUtil.toggle();
sendAction("toggle-dnd", dndUtil.dnd, dndUtil.newSettings);
},
},
{
label: t.__("Desktop Settings"),
accelerator: "Ctrl+,",
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("open-settings");
}
},
},
{
label: t.__("Keyboard Shortcuts"),
accelerator: "Ctrl+Shift+K",
enabled: enableMenu,
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("show-keyboard-shortcuts");
}
},
},
{
type: "separator",
},
{
label: t.__("Copy Zulip URL"),
accelerator: "Ctrl+Shift+C",
enabled: enableMenu,
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("copy-zulip-url");
}
},
},
{
label: t.__("Log Out of Organization"),
enabled: enableMenu,
click(_item, focusedWindow) {
if (focusedWindow) {
sendAction("log-out");
}
},
},
{
type: "separator",
},
{
label: t.__("Minimize"),
role: "minimize",
},
{
label: t.__("Close"),
role: "close",
},
{
label: t.__("Quit"),
role: "quit",
accelerator: "Ctrl+Q",
},
],
},
{
label: t.__("Edit"),
submenu: [
{
label: t.__("Undo"),
role: "undo",
},
{
label: t.__("Redo"),
role: "redo",
},
{
type: "separator",
},
{
label: t.__("Cut"),
role: "cut",
},
{
label: t.__("Copy"),
role: "copy",
},
{
label: t.__("Paste"),
role: "paste",
},
{
label: t.__("Paste and Match Style"),
role: "pasteAndMatchStyle",
},
{
type: "separator",
},
{
label: t.__("Select All"),
role: "selectAll",
},
],
},
{
label: t.__("View"),
submenu: getViewSubmenu(),
},
{
label: t.__("History"),
submenu: getHistorySubmenu(enableMenu),
},
{
label: t.__("Window"),
submenu: getWindowSubmenu(tabs, activeTabIndex),
},
{
label: t.__("Tools"),
submenu: getToolsSubmenu(),
},
{
label: t.__("Help"),
role: "help",
submenu: getHelpSubmenu(),
},
];
}
function sendAction<Channel extends keyof RendererMessage>(
channel: Channel,
...args: Parameters<RendererMessage[Channel]>
): void {
const win = BrowserWindow.getAllWindows()[0];
if (process.platform === "darwin") {
win.restore();
}
send(win.webContents, channel, ...args);
}
async function checkForUpdate(): Promise<void> {
await appUpdater(true);
}
function getNextServer(tabs: TabData[], activeTabIndex: number): number {
do {
activeTabIndex = (activeTabIndex + 1) % tabs.length;
} while (tabs[activeTabIndex]?.role !== "server");
return activeTabIndex;
}
function getPreviousServer(tabs: TabData[], activeTabIndex: number): number {
do {
activeTabIndex = (activeTabIndex - 1 + tabs.length) % tabs.length;
} while (tabs[activeTabIndex]?.role !== "server");
return activeTabIndex;
}
export function setMenu(props: MenuProps): void {
const tpl =
process.platform === "darwin" ? getDarwinTpl(props) : getOtherTpl(props);
const menu = Menu.buildFromTemplate(tpl);
Menu.setApplicationMenu(menu);
}

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

@@ -0,0 +1,145 @@
import type {ClientRequest, IncomingMessage, Session} from "electron/main";
import {app, net} from "electron/main";
import fs from "node:fs";
import path from "node:path";
import stream from "node:stream";
import util from "node:util";
import * as Sentry from "@sentry/electron";
import getStream from "get-stream";
import {z} from "zod";
import Logger from "../common/logger-util.js";
import * as Messages from "../common/messages.js";
import type {ServerConf} from "../common/types.js";
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 logger = new Logger({
file: "domain-util.log",
});
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) {
// eslint-disable-next-line no-bitwise, unicorn/prefer-code-point
hash = (hash * 33) ^ url.charCodeAt(--length);
}
// Create 'server-icons' directory if not existed
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
// eslint-disable-next-line no-bitwise
return `${dir}/${hash >>> 0}${extension}`;
};
export const _getServerSettings = async (
domain: string,
session: 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 data: unknown = JSON.parse(await getStream(response));
/* eslint-disable @typescript-eslint/naming-convention */
const {
realm_name,
realm_uri,
realm_icon,
zulip_version,
zulip_feature_level,
} = z
.object({
realm_name: z.string(),
realm_uri: z.string().url(),
realm_icon: z.string(),
zulip_version: z.string().default("unknown"),
zulip_feature_level: z.number().default(0),
})
.parse(data);
/* eslint-enable @typescript-eslint/naming-convention */
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: realm_name,
zulipVersion: zulip_version,
zulipFeatureLevel: zulip_feature_level,
};
};
export const _saveServerIcon = async (
url: string,
session: Session,
): Promise<string | null> => {
try {
const response = await fetchResponse(net.request({url, session}));
if (response.statusCode !== 200) {
logger.log("Could not get server icon.");
return null;
}
const filePath = generateFilePath(url);
await pipeline(response, fs.createWriteStream(filePath));
return filePath;
} catch (error: unknown) {
logger.log("Could not get server icon.");
logger.log(error);
Sentry.captureException(error);
return null;
}
};
/* Request: reconnect-util */
export const _isOnline = async (
url: string,
session: Session,
): Promise<boolean> => {
try {
const response = await fetchResponse(
net.request({
method: "HEAD",
url: `${url}/api/v1/server_settings`,
session,
}),
);
const isValidResponse =
response.statusCode >= 200 && response.statusCode < 400;
return isValidResponse;
} catch (error: unknown) {
logger.log(error);
return false;
}
};

22
app/main/sentry.ts Normal file
View File

@@ -0,0 +1,22 @@
import {app} from "electron/main";
import * as Sentry from "@sentry/electron/main"; // eslint-disable-line n/file-extension-in-import
import {getConfigItem} from "../common/config-util.js";
export const sentryInit = (): void => {
Sentry.init({
dsn: "https://628dc2f2864243a08ead72e63f94c7b1@o48127.ingest.sentry.io/204668",
// Don't report errors in development or if disabled by the user.
beforeSend: (event) =>
app.isPackaged && getConfigItem("errorReporting", true) ? event : null,
// We should ignore this error since it's harmless and we know the reason behind this
// This error mainly comes from the console logs.
// This is a temp solution until Sentry supports disabling the console logs
ignoreErrors: ["does not appear to be a valid Zulip server"],
/// sendTimeout: 30 // wait 30 seconds before considering the sending capture to have failed, default is 1 second
});
};

36
app/main/startup.ts Normal file
View File

@@ -0,0 +1,36 @@
import {app} from "electron/main";
import process from "node:process";
import AutoLaunch from "auto-launch";
import * as ConfigUtil from "../common/config-util.js";
export const setAutoLaunch = async (
AutoLaunchValue: boolean,
): Promise<void> => {
// Don't run this in development
if (!app.isPackaged) {
return;
}
const autoLaunchOption = ConfigUtil.getConfigItem(
"startAtLogin",
AutoLaunchValue,
);
// `setLoginItemSettings` doesn't support linux
if (process.platform === "linux") {
const zulipAutoLauncher = new AutoLaunch({
name: "Zulip",
isHidden: false,
});
await (autoLaunchOption
? zulipAutoLauncher.enable()
: zulipAutoLauncher.disable());
} else {
app.setLoginItemSettings({
openAtLogin: autoLaunchOption,
openAsHidden: false,
});
}
};

View File

@@ -0,0 +1,75 @@
import type {
IpcMainEvent,
IpcMainInvokeEvent,
WebContents,
} from "electron/main";
import {
ipcMain as untypedIpcMain, // eslint-disable-line no-restricted-imports
} from "electron/main";
import type {
MainCall,
MainMessage,
RendererMessage,
} from "../common/typed-ipc.js";
type MainListener<Channel extends keyof MainMessage> =
MainMessage[Channel] extends (...args: infer Args) => infer Return
? (event: IpcMainEvent & {returnValue: Return}, ...args: Args) => void
: never;
type MainHandler<Channel extends keyof MainCall> = MainCall[Channel] extends (
...args: infer Args
) => infer Return
? (event: IpcMainInvokeEvent, ...args: Args) => Return | Promise<Return>
: never;
export const ipcMain: {
on(
channel: "forward-message",
listener: <Channel extends keyof RendererMessage>(
event: IpcMainEvent,
channel: Channel,
...args: Parameters<RendererMessage[Channel]>
) => void,
): void;
on<Channel extends keyof MainMessage>(
channel: Channel,
listener: MainListener<Channel>,
): void;
once<Channel extends keyof MainMessage>(
channel: Channel,
listener: MainListener<Channel>,
): void;
removeListener<Channel extends keyof MainMessage>(
channel: Channel,
listener: MainListener<Channel>,
): void;
removeAllListeners(channel?: keyof MainMessage): void;
handle<Channel extends keyof MainCall>(
channel: Channel,
handler: MainHandler<Channel>,
): void;
handleOnce<Channel extends keyof MainCall>(
channel: Channel,
handler: MainHandler<Channel>,
): void;
removeHandler(channel: keyof MainCall): void;
} = untypedIpcMain;
export function send<Channel extends keyof RendererMessage>(
contents: WebContents,
channel: Channel,
...args: Parameters<RendererMessage[Channel]>
): void {
contents.send(channel, ...args);
}
export function sendToFrame<Channel extends keyof RendererMessage>(
contents: WebContents,
frameId: number | [number, number],
channel: Channel,
...args: Parameters<RendererMessage[Channel]>
): void {
contents.sendToFrame(frameId, channel, ...args);
}

View File

@@ -1,41 +0,0 @@
{
"name": "zulip",
"productName": "Zulip",
"version": "1.3.0-beta",
"description": "Zulip Desktop App",
"license": "Apache-2.0",
"email": "<svnitakash@gmail.com>",
"copyright": "©2017 Kandra Labs, Inc.",
"author": {
"name": "Kandra Labs, Inc.",
"email": "svnitakash@gmail.com"
},
"repository": {
"type": "git",
"url": "https://github.com/zulip/zulip-electron.git"
},
"bugs": {
"url": "https://github.com/zulip/zulip-electron/issues"
},
"main": "main/index.js",
"keywords": [
"Zulip",
"Group Chat app",
"electron-app",
"electron",
"Desktop app",
"InstantMessaging"
],
"dependencies": {
"electron-debug": "1.4.0",
"electron-is-dev": "0.3.0",
"electron-localshortcut": "2.0.2",
"electron-log": "2.2.7",
"electron-spellchecker": "1.2.0",
"electron-updater": "2.8.5",
"node-json-db": "0.7.3",
"request": "2.81.0",
"wurl": "2.5.0",
"electron-window-state": "4.1.1"
}
}

View File

@@ -1,33 +1,26 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="css/about.css">
</head>
<body>
<div class="about">
<img class="logo" src="../resources/zulip.png" />
<p class="detail" id="version">version ?.?.?</p>
<div class="maintenance-info">
<p class="detail maintainer">Maintained by Zulip</p>
<p class="detail license">Available under the Apache License</p>
<a class="bug" onclick="linkInBrowser()" href="#">Found bug?</a>
</div>
</div>
<script>
<meta charset="UTF-8" />
<link rel="stylesheet" href="css/about.css" />
const app = require('electron').remote.app;
const version_tag = document.getElementById('version');
version_tag.innerHTML = 'version ' + app.getVersion();
function linkInBrowser(event) {
const shell = require('electron').shell;
const url = "https://github.com/zulip/zulip-electron/issues/new?body=Please describe your issue and steps to reproduce it."
shell.openExternal(url);
}
</script>
</body>
</html>
<!-- Initially hidden to prevent FOUC -->
<div class="about" hidden>
<img class="logo" src="../resources/zulip.png" />
<p class="detail" id="version"></p>
<div class="maintenance-info">
<p class="detail maintainer">
Maintained by
<a href="https://zulip.com" target="_blank" rel="noopener noreferrer"
>Zulip</a
>
</p>
<p class="detail license">
Available under the
<a
href="https://github.com/zulip/zulip-desktop/blob/main/LICENSE"
target="_blank"
rel="noopener noreferrer"
>Apache 2.0 License</a
>
</p>
</div>
</div>

View File

@@ -1,73 +1,69 @@
body {
background: #fafafa;
font-family: menu, "Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased;
:host {
contain: strict;
display: flow-root;
background: rgb(250 250 250 / 100%);
font-family: menu, "Helvetica Neue", sans-serif;
-webkit-font-smoothing: subpixel-antialiased;
}
.logo {
display: block;
margin: 0 auto;
display: block;
margin: -40px auto;
}
#version {
color: #aaa;
font-size: 0.9em;
color: rgb(68 67 67 / 100%);
font-size: 1.3em;
padding-top: 40px;
}
.about {
margin-top: 50px;
text-align: center;
display: block !important;
margin: 25vh auto;
height: 25vh;
text-align: center;
}
.about p {
font-size: 20px;
color: rgba(0, 0, 0, 0.62);
font-size: 20px;
color: rgb(0 0 0 / 62%);
}
.about img {
width: 150px;
width: 150px;
}
.detail {
text-align: center;
text-align: center;
}
.detail.maintainer {
font-size: 1.2em;
font-weight: 500;
font-size: 1.2em;
font-weight: 500;
}
.detail.license {
font-size: 0.8em;
font-size: 0.8em;
}
.maintenance-info {
position: absolute;
width: 100%;
bottom: 20px;
left: 0px;
color: #444;
cursor: pointer;
position: absolute;
width: 100%;
left: 0;
color: rgb(68 68 68 / 100%);
}
.maintenance-info p {
margin: 0;
font-size: 1em;
width: 100%;
margin: 0;
font-size: 1em;
width: 100%;
}
.maintenance-info .bug {
display: inline-block;
padding: 8px 15px;
margin-top: 30px;
text-decoration: none;
background-color: #52c2af;
color: #fff;
border-radius: 4px;
transition: background-color 0.2s ease;
p.detail a {
color: rgb(53 95 76 / 100%);
}
.maintenance-info .bug:hover {
background-color: #32a692;
p.detail a:hover {
text-decoration: underline;
}

View File

@@ -0,0 +1,19 @@
:host {
--button-color: rgb(69 166 149);
}
button {
background-color: var(--button-color);
border-color: var(--button-color);
}
button:hover,
button:focus {
border-color: var(--button-color);
color: var(--button-color);
}
button:active {
background-color: rgb(241 241 241);
color: var(--button-color);
}

View File

@@ -0,0 +1,12 @@
@font-face {
font-family: "Material Icons";
font-style: normal;
font-weight: 400;
src: local("Material Icons"), local("MaterialIcons-Regular"),
url("../fonts/MaterialIcons-Regular.ttf") format("truetype");
}
@font-face {
font-family: Montserrat;
src: url("../fonts/Montserrat-Regular.ttf") format("truetype");
}

View File

@@ -4,318 +4,481 @@
html,
body {
height: 100%;
margin: 0;
cursor: default;
user-select: none;
height: 100%;
margin: 0;
cursor: default;
user-select: none;
}
#content {
display: flex;
height: 100%;
background: #eee url(../img/ic_loading.gif) no-repeat;
background-size: 60px 60px;
background-position: center;
display: flex;
height: 100%;
}
#sidebar {
background: #222c31;
width: 54px;
padding: 27px 0 20px 0;
justify-content: space-between;
display: flex;
flex-direction: column;
-webkit-app-region: drag;
overflow: hidden;
.toggle-sidebar {
background: rgb(34 44 49 / 100%);
width: 54px;
padding: 27px 0 20px;
justify-content: space-between;
display: flex;
flex-direction: column;
-webkit-app-region: drag;
overflow: hidden;
transition: all 0.5s ease;
z-index: 2;
}
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: local('Material Icons'), local('MaterialIcons-Regular'), url(../fonts/MaterialIcons-Regular.ttf) format('truetype');
.toggle-sidebar div {
transition: all 0.5s ease-out;
}
.sidebar-hide {
width: 0;
transition: all 0.8s ease;
}
.sidebar-hide div {
transform: translateX(-100%);
transition: all 0.6s ease-out;
}
#view-controls-container {
height: calc(100% - 208px);
overflow-y: hidden;
}
#view-controls-container::-webkit-scrollbar {
width: 4px;
}
#view-controls-container::-webkit-scrollbar-track {
box-shadow: inset 0 0 6px rgb(0 0 0 / 30%);
}
#view-controls-container::-webkit-scrollbar-thumb {
background-color: rgb(169 169 169 / 100%);
outline: 1px solid rgb(169 169 169 / 100%);
}
#view-controls-container:hover {
overflow-y: overlay;
}
/*******************
* Left Sidebar *
*******************/
* Left Sidebar *
*******************/
#tabs-container {
display: flex;
align-items: center;
flex-direction: column;
display: flex;
align-items: center;
flex-direction: column;
}
.material-icons {
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
/* Preferred icon size */
font-size: 24px;
display: inline-block;
line-height: 1;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
/* Support for all WebKit browsers. */
-webkit-font-smoothing: antialiased;
/* Support for Safari and Chrome. */
text-rendering: optimizeLegibility;
font-family: "Material Icons";
font-weight: normal;
font-style: normal;
/* Preferred icon size */
font-size: 24px;
display: inline-block;
line-height: 1;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
/* Support for all WebKit browsers. */
-webkit-font-smoothing: antialiased;
/* Support for Safari and Chrome. */
text-rendering: optimizelegibility;
}
#actions-container {
display: flex;
flex-direction: column;
position: fixed;
bottom: 0;
}
.action-button {
display: flex;
flex-direction: column;
align-items: center;
padding: 10px;
display: flex;
flex-direction: column;
padding: 12px;
}
.action-button:hover {
cursor: pointer;
cursor: pointer;
}
.action-button i {
color: #6c8592;
font-size: 28px;
color: rgb(108 133 146 / 100%);
font-size: 28px;
}
.action-button:hover i {
color: #98a9b3;
color: rgb(152 169 179 / 100%);
}
.tab:first-child {
margin-top: 8px;
.action-button.active {
/* background-color: rgba(255, 255, 255, 0.25); */
background-color: rgb(239 239 239 / 100%);
opacity: 0.9;
padding-right: 14px;
}
.action-button.active i {
color: rgb(28 38 43 / 100%);
}
.action-button.disable {
opacity: 0.6;
}
.action-button.disable:hover {
cursor: not-allowed;
}
.action-button.disable:hover i {
color: rgb(108 133 146 / 100%);
}
.tab {
position: relative;
margin: 2px 0;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
position: relative;
margin: 2px 0;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
.tab:first-child {
margin-top: 9px;
}
.tab .server-icons {
border-radius: 50%;
width: 30px;
padding: 3px;
height: 30px;
vertical-align: top;
width: 35px;
vertical-align: top;
border-radius: 4px;
}
.tab .server-tab {
width: 100%;
height: 35px;
position: relative;
margin: 5px 0 2px 0;
z-index: 11;
line-height: 31px;
color: #eee;
text-align: center;
overflow: hidden;
opacity: 0.6;
padding: 2px 0;
width: 100%;
height: 35px;
position: relative;
margin-top: 5px;
z-index: 11;
line-height: 31px;
color: rgb(238 238 238 / 100%);
text-align: center;
overflow: hidden;
opacity: 0.6;
padding: 6px 0;
}
.server-tab .alt-icon {
font-family: Verdana, sans-serif;
font-weight: 600;
font-size: 22px;
border: 2px solid rgb(34 44 49 / 100%);
margin-left: 17%;
width: 35px;
border-radius: 4px;
}
.tab .server-tab:hover {
opacity: 0.8;
}
.tab.functional-tab {
height: 46px;
padding: 0;
}
.tab.functional-tab.active .server-tab {
padding: 2px 0;
height: 40px;
background-color: rgba(255, 255, 255, 0.25);
}
.tab.functional-tab .server-tab i {
font-size: 28px;
line-height: 36px;
opacity: 0.8;
}
.tab.active .server-tab {
opacity: 1;
background-color: #648478;
opacity: 1;
background-color: rgb(100 132 120 / 100%);
}
.tab.functional-tab {
height: 46px;
padding: 0;
}
.tab.functional-tab.active .server-tab {
padding: 2px 0;
height: 40px;
background-color: rgb(255 255 255 / 25%);
}
.tab.functional-tab .server-tab i {
font-size: 28px;
line-height: 36px;
}
.tab .server-tab-badge.active {
border-radius: 9px;
min-width: 11px;
padding: 0 3px;
height: 17px;
background-color: #f44336;
font-size: 10px;
font-family: sans-serif;
position: absolute;
right: 5px;
z-index: 15;
top: 6px;
float: right;
color: #fff;
text-align: center;
line-height: 17px;
display: block;
right: 0;
border-radius: 9px;
min-width: 11px;
padding: 0 3px;
height: 17px;
background-color: rgb(244 67 54 / 100%);
font-size: 10px;
font-family: sans-serif;
position: absolute;
z-index: 15;
top: 6px;
float: right;
color: rgb(255 255 255 / 100%);
text-align: center;
line-height: 17px;
display: block;
right: 0;
}
.tab .server-tab-badge {
display: none;
display: none;
}
.tab .server-tab-badge.close-button {
width: 16px;
padding: 0;
width: 16px;
padding: 0;
}
.tab .server-tab-badge.close-button i {
font-size: 13px;
line-height: 17px;
font-size: 13px;
line-height: 17px;
}
#add-tab {
display: flex;
align-items: center;
flex-direction: column;
display: flex;
align-items: center;
flex-direction: column;
}
.tab .server-tab-shortcut {
color: #648478;
font-size: 12px;
text-align: center;
font-family: sans-serif;
margin-bottom: 5px;
color: rgb(100 132 120 / 100%);
font-size: 12px;
text-align: center;
font-family: sans-serif;
margin-bottom: 5px;
}
.refresh {
animation: rotate-loader 1s linear infinite;
}
@keyframes rotate-loader {
from {
transform: rotate(0);
}
to {
transform: rotate(-360deg);
}
}
/*******************
* Webview Area *
*******************/
* Webview Area *
*******************/
#webviews-container {
display: flex;
height: 100%;
width: 100%;
display: flex;
height: 100%;
width: 100%;
}
webview {
opacity: 1;
transition: opacity 0.3s ease-in;
flex-grow: 1;
/* Pseudo element for loading indicator */
#webviews-container::before {
content: "";
position: absolute;
z-index: 1;
background: rgb(255 255 255 / 100%) url("../img/ic_loading.gif") no-repeat;
background-size: 60px 60px;
background-position: center;
width: 100%;
height: 100%;
}
webview.onload {
transition: opacity 1s cubic-bezier(0.95, 0.05, 0.795, 0.035);
/* When the active webview is loaded */
#webviews-container.loaded::before {
z-index: -1;
visibility: hidden;
}
webview.disabled {
flex: 0 1;
height: 0;
width: 0;
opacity: 0;
transition: opacity 0.3s ease-out;
.webview-pane,
.functional-view {
position: absolute;
width: 100%;
height: 100%;
flex-grow: 1;
visibility: hidden;
}
webview:focus {
outline: 0px solid transparent;
.webview-pane {
display: flex;
flex-direction: column;
}
.webview-pane > webview {
flex: 1;
}
.webview-pane.active,
.functional-view.active {
z-index: 1;
visibility: visible;
}
webview.focus {
outline: 0 solid transparent;
}
.webview-unsupported {
background: rgb(254 243 199);
border: 1px solid rgb(253 230 138);
color: rgb(69 26 3);
font-family: system-ui;
font-size: 14px;
display: flex;
}
.webview-unsupported[hidden] {
display: none;
}
.webview-unsupported-message {
padding: 0.3em;
flex: 1;
text-align: center;
}
.webview-unsupported-dismiss {
padding: 0.3em;
cursor: pointer;
}
/* Tooltip styling */
#loading-tooltip,
#dnd-tooltip,
#back-tooltip,
#reload-tooltip,
#setting-tooltip {
font-family: sans-serif;
background: #222c31;
margin-left: 68px;
padding: 6px 8px;
position: absolute;
margin-top: 0px;
z-index: 1000;
color: white;
border-radius: 4px;
text-align: center;
width: 55px;
font-size: 14px;
font-family: sans-serif;
background: rgb(34 44 49 / 100%);
margin-left: 48px;
padding: 6px 8px;
position: absolute;
margin-top: 0;
z-index: 1000;
color: rgb(255 255 255 / 100%);
border-radius: 4px;
text-align: center;
width: 55px;
font-size: 14px;
}
#reload-tooltip:after,
#setting-tooltip:after {
content: " ";
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
border-right: 8px solid #222c31;
position: absolute;
top: 7px;
right: 68px;
#loading-tooltip::after,
#dnd-tooltip::after,
#back-tooltip::after,
#reload-tooltip::after,
#setting-tooltip::after {
content: " ";
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
border-right: 8px solid rgb(34 44 49 / 100%);
position: absolute;
top: 7px;
right: 68px;
}
#add-server-tooltip,
.server-tooltip {
font-family: arial, sans-serif;
background: rgb(34 44 49 / 100%);
left: 56px;
padding: 10px 20px;
position: fixed;
margin-top: 11px;
z-index: 5000 !important;
color: rgb(255 255 255 / 100%);
border-radius: 4px;
text-align: center;
width: max-content;
font-size: 14px;
}
#add-server-tooltip::after,
.server-tooltip::after {
content: " ";
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
border-right: 8px solid rgb(34 44 49 / 100%);
position: absolute;
top: 10px;
left: -5px;
}
#collapse-button {
bottom: 30px;
left: 20px;
position: absolute;
width: 24px;
height: 24px;
background: #222c31;
border-radius: 20px;
cursor: pointer;
box-shadow: #999 1px 1px;
bottom: 30px;
left: 20px;
position: absolute;
width: 24px;
height: 24px;
background: rgb(34 44 49 / 100%);
border-radius: 20px;
cursor: pointer;
box-shadow: rgb(153 153 153 / 100%) 1px 1px;
}
#collapse-button i {
color: #efefef;
color: rgb(239 239 239 / 100%);
}
#main-container {
display: flex;
height: 100%;
width: 100%;
position: relative;
display: flex;
height: 100%;
width: 100%;
position: relative;
flex-grow: 1;
flex-basis: 0;
}
.hidden {
display: none !important;
display: none !important;
}
/* Full screen Popup container */
.popup .popuptext {
visibility: hidden;
background-color: #555;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 9px 0;
position: absolute;
z-index: 1000;
font-family: arial;
width: 240px;
top: 15px;
height: 20px;
left: 43%;
visibility: hidden;
background-color: rgb(85 85 85 / 100%);
color: rgb(255 255 255 / 100%);
text-align: center;
border-radius: 6px;
padding: 9px 0;
position: absolute;
z-index: 1000;
font-family: arial, sans-serif;
width: 240px;
top: 15px;
height: 20px;
left: 43%;
}
.popup .show {
visibility: visible;
animation: cssAnimation 0s ease-in 5s forwards;
animation-fill-mode: forwards;
visibility: visible;
animation: full-screen-popup 0s ease-in 1s forwards;
animation-fill-mode: forwards;
}
@keyframes cssAnimation {
from {
opacity: 0;
}
to {
width: 0;
height: 0;
overflow: hidden;
opacity: 1;
}
@keyframes full-screen-popup {
from {
opacity: 0;
}
to {
width: 0;
height: 0;
overflow: hidden;
opacity: 1;
}
}

View File

@@ -1,42 +1,59 @@
html, body {
margin: 0;
cursor: default;
font-size: 14px;
color: #333;
background: #fff;
user-select:none;
html,
body {
margin: 0;
cursor: default;
font-size: 14px;
color: rgb(51 51 51 / 100%);
background: rgb(255 255 255 / 100%);
user-select: none;
}
#content {
display: flex;
flex-direction: column;
font-family: "Trebuchet MS", Helvetica, sans-serif;
margin: 100px 200px;
text-align: center;
display: flex;
flex-direction: column;
font-family: "Trebuchet MS", Helvetica, sans-serif;
margin: 100px 200px;
text-align: center;
}
#title {
font-size: 24px;
font-weight: bold;
margin: 20px 0;
text-align: left;
font-size: 24px;
font-weight: bold;
margin: 20px 0;
}
#subtitle {
font-size: 20px;
text-align: left;
margin: 12px 0;
}
#description {
font-size: 16px;
text-align: left;
font-size: 16px;
list-style-position: inside;
}
#reconnect {
font-size: 16px;
background: #009688;
color: #fff;
width: 84px;
height: 32px;
border-radius: 5px;
line-height: 32px;
margin: 20px auto 0;
cursor: pointer;
float: left;
}
#reconnect:hover {
opacity: 0.8;
}
#settings {
margin-left: 116px;
}
.button {
font-size: 16px;
background: rgb(0 150 136 / 100%);
color: rgb(255 255 255 / 100%);
width: 96px;
height: 32px;
border-radius: 5px;
line-height: 32px;
cursor: pointer;
}
.button:hover {
opacity: 0.8;
}

View File

@@ -1,26 +1,67 @@
html,
body {
height: 100%;
margin: 0;
cursor: default;
font-size: 14px;
color: #333;
background: #efefef;
@import url("@yaireo/tagify/dist/tagify.css");
:host {
contain: strict;
display: flow-root;
cursor: default;
user-select: none;
font-family: menu, "Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 14px;
background: rgb(239 239 239 / 100%);
letter-spacing: -0.08px;
line-height: 18px;
color: rgb(139 142 143 / 100%);
/* Copied from https://github.com/yairEO/tagify/blob/v4.17.7/src/tagify.scss#L4-L8 */
--tagify-dd-color-primary: rgb(53 149 246);
--tagify-dd-bg-color: rgb(255 255 255);
--tagify-dd-item-pad: 0.3em 0.5em;
}
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: local('Material Icons'),
local('MaterialIcons-Regular'),
url(../fonts/MaterialIcons-Regular.ttf) format('truetype');
kbd {
display: inline-block;
border: 1px solid rgb(204 204 204 / 100%);
border-radius: 4px;
font-size: 15px;
font-family: "Courier New", Courier, monospace;
font-weight: bold;
white-space: nowrap;
background-color: rgb(247 247 247 / 100%);
color: rgb(51 51 51 / 100%);
margin: 0 0.1em;
padding: 0.3em 0.8em;
text-shadow: 0 1px 0 rgb(255 255 255 / 100%);
line-height: 1.4;
}
table,
th,
td {
border-collapse: collapse;
color: rgb(56 52 48 / 100%);
}
table {
width: 100%;
margin-top: 18px;
margin-bottom: 18px;
}
td {
padding: 5px;
}
td:nth-child(odd) {
text-align: right;
width: 50%;
}
.material-icons {
font-family: 'Material Icons';
font-family: "Material Icons";
font-weight: normal;
font-style: normal;
/* Preferred icon size */
font-size: 24px;
display: inline-block;
@@ -30,205 +71,699 @@ body {
word-wrap: normal;
white-space: nowrap;
direction: ltr;
/* Support for all WebKit browsers. */
-webkit-font-smoothing: antialiased;
/* Support for Safari and Chrome. */
text-rendering: optimizeLegibility;
text-rendering: optimizelegibility;
}
#content {
display: flex;
height: 100%;
font-family: sans-serif;
display: flex !important;
height: 100%;
font-family: Montserrat, sans-serif;
}
#sidebar {
width: 80px;
padding: 30px;
display: flex;
flex-direction: column;
font-size: 16px;
width: 150px;
min-width: 100px;
padding: 30px 30px 30px 35px;
display: flex;
flex-direction: column;
font-size: 16px;
background: rgb(242 242 242 / 100%);
}
#nav-container {
padding: 20px 0;
padding: 20px 0;
}
.nav {
padding: 5px 0;
color: #999;
cursor: pointer;
padding: 7px 0;
color: rgb(153 153 153 / 100%);
cursor: pointer;
}
.nav.active {
color: #464e5a;
cursor: default;
position: relative;
color: rgb(78 191 172 / 100%);
cursor: default;
position: relative;
}
.nav.active::before {
background: #464e5a;
width: 3px;
height: 16px;
position: absolute;
left: -8px;
content: '';
background: rgb(70 78 90 / 100%);
width: 3px;
height: 18px;
position: absolute;
left: -8px;
content: "";
}
/* We don't want to show this in nav item since we have the + button for adding an Organization */
/* stylelint-disable-next-line selector-id-pattern */
#nav-AddServer {
display: none;
}
#settings-header {
font-size: 22px;
color: #5c6166;
font-size: 22px;
color: rgb(34 44 49 / 100%);
font-weight: bold;
text-transform: uppercase;
}
#settings-container {
width: 100%;
display: flex;
padding: 30px;
overflow-y: scroll;
width: 100%;
display: flex;
padding: 30px;
overflow-y: scroll;
}
#new-server-container {
margin: 20px 0;
opacity: 1;
transition: opacity 0.3s;
padding-left: 42px;
padding-top: 25px;
margin-right: 16px;
}
.title {
padding: 4px 0 6px 0;
font-weight: bold;
color: #1e1e1e;
font-weight: 500;
color: rgb(34 44 49 / 100%);
}
.page-title {
color: rgb(34 44 49 / 100%);
font-size: 15px;
font-weight: bold;
padding: 4px 0 6px;
}
.add-server-info-row {
display: flex;
margin: 8px 0 0;
}
.add-server-info-right {
flex-grow: 1;
margin-top: 10px;
}
.sub-title {
padding: 4px 0 6px 0;
font-weight: bold;
color: #616161;
padding: 4px 0 6px;
font-weight: bold;
color: rgb(97 97 97 / 100%);
}
img.server-info-icon {
width: 36px;
height: 36px;
padding: 4px;
width: 36px;
height: 36px;
padding: 4px 4px 4px 10px;
cursor: pointer;
vertical-align: middle;
}
.server-info-left {
margin: 10px 20px 0 0;
margin: 4px 20px 0 0;
min-width: 40%;
}
.server-info-right {
flex-grow: 1;
margin-right: 10px;
margin-top: 4px;
width: 55%;
display: flex;
align-items: baseline;
justify-content: space-between;
flex-grow: 1;
}
.server-info-row {
display: flex;
margin: 8px 0 0 0;
display: inline-block;
margin: 5px 0 0;
}
.server-info-left .server-info-row {
display: inline-flex;
align-items: inherit;
vertical-align: -2px;
position: relative;
}
.server-url {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.server-info-alias {
font-weight: bold;
cursor: pointer;
font-weight: bold;
cursor: pointer;
}
.server-info-url {
flex-grow: 1;
font-size: 14px;
height: 24px;
border: none;
border-bottom: #ededed 1px solid;
outline-width: 0;
background: transparent;
max-width: 500px;
.server-url-info {
margin-right: 10px;
}
.server-info-value:focus {
border-bottom: #388E3C 1px solid;
.setting-input-key {
font-size: 14px;
height: 27px;
line-height: 27px;
font-weight: bold;
background: transparent;
flex-wrap: nowrap;
margin-right: 10px;
}
.setting-input-value {
flex-grow: 1;
font-size: 14px;
border-radius: 4px;
padding: 13px;
border: rgb(237 237 237 / 100%) 2px solid;
outline-width: 0;
background: transparent;
max-width: 450px;
}
.setting-input-value:focus {
border: rgb(78 191 172 / 100%) 2px solid;
}
.invalid-input-value:focus {
border: rgb(239 83 80 / 100%) 2px solid;
}
.manual-proxy-block {
width: 96%;
}
.actions-container {
display: flex;
font-size: 14px;
color: #235d3a;
vertical-align: middle;
margin: 10px 0;
flex-wrap: wrap;
display: flex;
font-size: 14px;
color: rgb(35 93 58 / 100%);
vertical-align: middle;
margin: 10px 0;
flex-wrap: wrap;
}
.action {
display: flex;
align-items: center;
padding: 0 10px;
border-radius: 2px;
margin-right: 10px;
display: flex;
align-items: center;
padding: 0 10px;
margin-right: 10px;
}
.action i {
margin-right: 5px;
font-size: 18px;
line-height: 27px;
margin-right: 5px;
font-size: 18px;
line-height: 26px;
}
.settings-pane {
flex-grow: 1;
flex-grow: 1;
min-width: 550px;
}
.action:hover {
cursor: pointer;
}
.action.disabled:hover {
cursor: default;
cursor: pointer;
}
.action.disabled {
color: #999;
color: rgb(153 153 153 / 100%);
}
.action.disabled:hover {
cursor: default;
}
.settings-card {
display: flex;
padding: 16px 30px;
margin: 10px 0 20px 0;
background: #fff;
border-radius: 2px;
width: 540px;
box-shadow: 1px 2px 4px #bcbcbc;
display: flex;
flex-wrap: wrap;
padding: 12px 30px;
margin: 10px 0 20px;
background: rgb(255 255 255 / 100%);
width: 80%;
transition: all 0.2s;
}
.settings-card:hover {
box-shadow: 0 2px 5px 0 rgb(0 0 0 / 16%), 0 2px 0 0 rgb(0 0 0 / 12%);
}
.hidden {
display: none;
margin: 5px !important;
display: none;
margin: 5px !important;
}
.red {
color: #ef5350;
background: #ffebee;
border: 1px solid #ef5350;
color: rgb(240 148 148);
background: rgb(255 255 255 / 100%);
border-radius: 4px;
display: inline-block;
border: 2px solid rgb(240 148 148);
padding: 10px;
width: 100px;
cursor: pointer;
font-size: 1rem;
font-weight: bold;
transition: background-color 0.2s ease;
text-decoration: none;
text-align: center;
}
.red:hover {
background-color: rgb(240 148 148);
color: rgb(255 255 255 / 100%);
}
.green {
color: #388E3C;
background: #E8F5E9;
border: 1px solid #388E3C;
color: rgb(255 255 255 / 100%);
background: rgb(78 191 172 / 100%);
border-radius: 4px;
display: inline-block;
border: none;
padding: 10px;
width: 100px;
cursor: pointer;
font-size: 1rem;
font-weight: bold;
transition: background-color 0.2s ease;
text-decoration: none;
text-align: center;
}
.green:hover {
background-color: rgb(60 159 141 / 100%);
color: rgb(255 255 255 / 100%);
}
.w-150 {
width: 150px;
}
.w-250 {
width: 250px;
}
.grey {
color: #9E9E9E;
background: #FAFAFA;
border: 1px solid #9E9E9E;
color: rgb(158 158 158 / 100%);
background: rgb(250 250 250 / 100%);
border: 1px solid rgb(158 158 158 / 100%);
}
.setting-row {
display: flex;
justify-content: space-between;
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
margin: 6px;
}
#note {
font-size: 10px;
}
.code {
font-family: Courier New, Courier, monospace;
font-family: "Courier New", Courier, monospace;
}
i.open-tab-button {
padding: 0 5px;
font-size: 18px;
cursor: pointer;
}
padding-left: 5px;
font-size: 19px;
cursor: pointer;
}
#css-delete-action {
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
/*
.action class will add extra margin to right which
we don't want for a button; the extra margin is intended for radio buttons
*/
margin-right: 0;
}
.selected-css-path,
.download-folder-path {
background: rgb(238 238 238 / 100%);
padding: 5px 10px;
margin-right: 10px;
display: flex;
width: 90%;
justify-content: space-between;
}
#remove-custom-css {
align-items: flex-end;
}
#server-info-container {
/* min-height: calc(100% - 260px); */
}
#new-org-button {
margin: 30px 0;
}
#create-organization-container {
font-size: 1.15em;
}
#create-organization-container i {
position: relative;
top: 3px;
}
#open-create-org-link {
cursor: pointer;
text-decoration: none;
}
.toggle {
position: absolute;
margin-left: -9999px;
visibility: hidden;
}
.toggle + label {
display: block;
position: relative;
cursor: pointer;
outline: none;
user-select: none;
}
.disallowed:hover {
background-color: rgb(241 241 241 / 100%);
cursor: not-allowed;
}
input.toggle-round + label {
padding: 2px;
width: 50px;
height: 25px;
background-color: rgb(221 221 221 / 100%);
border-radius: 25px;
}
input.toggle-round + label::before,
input.toggle-round + label::after {
display: block;
position: absolute;
top: 2px;
left: 2px;
bottom: 2px;
content: "";
}
input.toggle-round + label::before {
background-color: rgb(241 241 241 / 100%);
border-radius: 25px;
inset: 0;
}
input.toggle-round + label::after {
width: 25px;
height: 25px;
background-color: rgb(255 255 255 / 100%);
border-radius: 100%;
}
input.toggle-round:checked + label::before {
background-color: rgb(78 191 172 / 100%);
inset: 0;
}
input.toggle-round:checked + label::after {
margin-left: 25px;
}
/* Add new server modal */
.add-server-modal {
display: block;
position: fixed;
z-index: 1;
padding-top: 15vh;
left: 0;
top: 0;
margin: auto;
width: 100%;
height: 100%;
/* background: rgba(61, 64, 67, 15); */
background: linear-gradient(
35deg,
rgb(0 59 82 / 100%),
rgb(69 181 155 / 100%)
);
overflow: auto;
}
/* Modal Content */
.modal-container {
background-color: rgb(244 247 248 / 100%);
margin: auto;
padding: 57px;
border: rgb(218 225 227 / 100%) 1px solid;
width: 550px;
height: 370px;
border-radius: 4px;
}
.add-server-modal .page-title {
text-align: center;
font-size: 1.6rem;
}
.divider {
margin-bottom: 30px;
margin-top: 30px;
color: rgb(125 135 138 / 100%);
}
.divider hr {
margin-left: 8px;
margin-right: 8px;
width: 44%;
}
.left {
float: left;
}
.right {
float: right;
}
.server-center {
width: 100%;
text-align: center;
align-items: center;
padding-top: 13px;
margin-left: -5px;
}
.server-center button {
font-weight: bold;
font-size: 1.1rem;
margin: auto;
align-items: center;
text-align: center;
color: rgb(255 255 255 / 100%);
background: rgb(78 191 172 / 100%);
border-color: none;
border: none;
width: 98%;
height: 46px;
border-radius: 4px;
cursor: pointer;
}
.server-center button:hover {
background: rgb(50 149 136 / 100%);
}
.server-center button:focus {
background: rgb(50 149 136 / 100%);
}
.certificates-card {
width: 80%;
}
.certificate-input {
width: 100%;
margin-top: 10px;
display: inline-flex;
}
.certificate-input div {
align-self: center;
}
.certificate-input .setting-input-value {
margin-left: 10px;
max-width: 100%;
}
.tip {
background: none;
padding: 0;
}
.tip:hover {
box-shadow: none;
}
.md-14 {
font-size: 14px;
vertical-align: middle;
padding-right: 6px;
}
#open-hotkeys-link {
text-decoration: underline;
cursor: pointer;
}
.server-network-option {
font-weight: bold;
font-size: 1.1rem;
margin-top: 10px;
padding-top: 15px;
align-items: center;
text-align: center;
color: rgb(78 191 172);
width: 98%;
height: 46px;
cursor: pointer;
}
i.open-network-button {
font-size: 16px;
cursor: pointer;
padding-left: 5px;
position: absolute;
}
/* responsive grid */
@media (width >= 500px) and (width <= 720px) {
#new-server-container {
padding-left: 0;
width: 60vw;
padding-right: 4vh;
}
.page-title {
width: 60vw;
}
}
@media (width <= 500px) {
#new-server-container {
padding-left: 0;
width: 54%;
}
.page-title {
width: 54%;
}
}
@media (width <= 650px) {
.selected-css-path,
.download-folder-path {
margin-right: 15px;
}
#css-delete-action {
margin-left: 10px;
}
#css-delete-action span {
display: none;
}
}
@media (width <= 720px) {
.modal-container {
width: 60vw;
padding: 40px;
min-width: 300px;
}
.server-center button {
margin-right: -12px;
width: 100%;
}
.divider {
margin-right: -8px;
}
.divider hr {
margin-left: 6px;
margin-right: 6px;
width: 43%;
}
}
@media (width <= 600px) {
.divider {
margin-left: 4%;
}
.divider hr {
margin-left: 2px;
margin-right: 2px;
width: 40%;
}
}
@media (width <= 900px) {
.settings-card {
flex-direction: column;
align-items: center;
}
.server-info-right {
flex-direction: column;
align-items: center;
}
.action {
margin-top: 10px;
}
}
.lang-menu {
font-size: 13px;
font-weight: bold;
background: rgb(78 191 172 / 100%);
width: 100px;
height: 38px;
color: rgb(255 255 255 / 100%);
border-color: rgb(0 0 0 / 0%);
}
/* stylelint-disable-next-line selector-class-pattern */
.tagify__input {
min-width: 130px !important;
}
/* stylelint-disable-next-line selector-class-pattern */
.tagify__input::before {
top: 0;
bottom: 0;
}
.settings-tagify-dropdown {
position: relative;
z-index: 9999;
height: 0;
}

View File

@@ -1,9 +1,9 @@
/* Override css rules */
.portico-wrap>.header {
display: none;
.portico-wrap > .header {
display: none;
}
.portico-container>.footer {
display: none;
}
.portico-container > .footer {
display: none;
}

Binary file not shown.

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

@@ -0,0 +1,67 @@
import {ipcRenderer} from "./typed-ipc-renderer.js";
// This helper is exposed via electron_bridge for use in the social
// login flow.
//
// It consists of a key and a promised token. The in-app page sends
// the key to the server, and opens the users browser to a page where
// they can log in and get a token encrypted to that key. When the
// user copies the encrypted token from their browser to the
// clipboard, we decrypt it and resolve the promise. The in-app page
// then uses the decrypted token to log the user in within the app.
//
// The encryption is authenticated (AES-GCM) to guarantee that we
// dont leak anything from the users clipboard other than the token
// intended for us.
export type ClipboardDecrypter = {
version: number;
key: Uint8Array;
pasted: Promise<string>;
};
export class ClipboardDecrypterImpl implements ClipboardDecrypter {
version: number;
key: Uint8Array;
pasted: Promise<string>;
constructor(_: number) {
// At this time, the only version is 1.
this.version = 1;
const {key, sig} = ipcRenderer.sendSync("new-clipboard-key");
this.key = key;
this.pasted = new Promise((resolve) => {
let interval: NodeJS.Timeout | null = null;
const startPolling = () => {
if (interval === null) {
interval = setInterval(poll, 1000);
}
void poll();
};
const stopPolling = () => {
if (interval !== null) {
clearInterval(interval);
interval = null;
}
};
const poll = async () => {
const plaintext = await ipcRenderer.invoke("poll-clipboard", key, sig);
if (plaintext === undefined) return;
window.removeEventListener("focus", startPolling);
window.removeEventListener("blur", stopPolling);
stopPolling();
resolve(plaintext);
};
window.addEventListener("focus", startPolling);
window.addEventListener("blur", stopPolling);
if (document.hasFocus()) {
startPolling();
}
});
}
}

View File

@@ -1,11 +0,0 @@
'use strict';
class BaseComponent {
generateNodeFromTemplate(template) {
const wrapper = document.createElement('div');
wrapper.innerHTML = template;
return wrapper.firstElementChild;
}
}
module.exports = BaseComponent;

View File

@@ -0,0 +1,12 @@
import type {Html} from "../../../common/html.js";
export function generateNodeFromHtml(html: Html): Element {
const wrapper = document.createElement("div");
wrapper.innerHTML = html.html;
if (wrapper.firstElementChild === null) {
throw new Error("No element found in HTML");
}
return wrapper.firstElementChild;
}

View File

@@ -0,0 +1,149 @@
import {clipboard} from "electron/common";
import type {WebContents} from "electron/main";
import type {
ContextMenuParams,
MenuItemConstructorOptions,
} from "electron/renderer";
import process from "node:process";
import {Menu} from "@electron/remote";
import * as t from "../../../common/translation-util.js";
export const contextMenu = (
webContents: WebContents,
event: Event,
props: ContextMenuParams,
) => {
const isText = props.selectionText !== "";
const isLink = props.linkURL !== "";
const linkUrl = isLink ? new URL(props.linkURL) : undefined;
const makeSuggestion = (suggestion: string) => ({
label: suggestion,
visible: true,
async click() {
await webContents.insertText(suggestion);
},
});
let menuTemplate: MenuItemConstructorOptions[] = [
{
label: t.__("Add to Dictionary"),
visible: props.isEditable && isText && props.misspelledWord.length > 0,
click(_item) {
webContents.session.addWordToSpellCheckerDictionary(
props.misspelledWord,
);
},
},
{
type: "separator",
visible: props.isEditable && isText && props.misspelledWord.length > 0,
},
{
label: `${t.__("Look Up")} "${props.selectionText}"`,
visible: process.platform === "darwin" && isText,
click(_item) {
webContents.showDefinitionForSelection();
},
},
{
type: "separator",
visible: process.platform === "darwin" && isText,
},
{
label: t.__("Cut"),
visible: isText,
enabled: props.isEditable,
accelerator: "CommandOrControl+X",
click(_item) {
webContents.cut();
},
},
{
label: t.__("Copy"),
accelerator: "CommandOrControl+C",
enabled: props.editFlags.canCopy,
click(_item) {
webContents.copy();
},
},
{
label: t.__("Paste"), // Bug: Paste replaces text
accelerator: "CommandOrControl+V",
enabled: props.isEditable,
click() {
webContents.paste();
},
},
{
type: "separator",
},
{
label:
linkUrl?.protocol === "mailto:"
? t.__("Copy Email Address")
: t.__("Copy Link"),
visible: isLink,
click(_item) {
clipboard.write({
bookmark: props.linkText,
text:
linkUrl?.protocol === "mailto:" ? linkUrl.pathname : props.linkURL,
});
},
},
{
label: t.__("Copy Image"),
visible: props.mediaType === "image",
click(_item) {
webContents.copyImageAt(props.x, props.y);
},
},
{
label: t.__("Copy Image URL"),
visible: props.mediaType === "image",
click(_item) {
clipboard.write({
bookmark: props.srcURL,
text: props.srcURL,
});
},
},
{
type: "separator",
visible: isLink || props.mediaType === "image",
},
{
label: t.__("Services"),
visible: process.platform === "darwin",
role: "services",
},
];
if (props.misspelledWord) {
if (props.dictionarySuggestions.length > 0) {
const suggestions: MenuItemConstructorOptions[] =
props.dictionarySuggestions.map((suggestion: string) =>
makeSuggestion(suggestion),
);
menuTemplate = [...suggestions, ...menuTemplate];
} else {
menuTemplate.unshift({
label: t.__("No Suggestion Found"),
enabled: false,
});
}
}
// Hide the invisible separators on Linux and Windows
// Electron has a bug which ignores visible: false parameter for separator menu items. So we remove them here.
// https://github.com/electron/electron/issues/5869
// https://github.com/electron/electron/issues/6906
const filteredMenuTemplate = menuTemplate.filter(
(menuItem) => menuItem.visible ?? true,
);
const menu = Menu.buildFromTemplate(filteredMenuTemplate);
menu.popup();
};

View File

@@ -1,43 +0,0 @@
'use strict';
const Tab = require(__dirname + '/../components/tab.js');
class FunctionalTab extends Tab {
template() {
return `<div class="tab functional-tab">
<div class="server-tab-badge close-button">
<i class="material-icons">close</i>
</div>
<div class="server-tab">
<i class="material-icons">${this.props.materialIcon}</i>
</div>
</div>`;
}
init() {
this.$el = this.generateNodeFromTemplate(this.template());
this.props.$root.appendChild(this.$el);
this.$closeButton = this.$el.getElementsByClassName('server-tab-badge')[0];
this.registerListeners();
}
registerListeners() {
super.registerListeners();
this.$el.addEventListener('mouseover', () => {
this.$closeButton.classList.add('active');
});
this.$el.addEventListener('mouseout', () => {
this.$closeButton.classList.remove('active');
});
this.$closeButton.addEventListener('click', e => {
this.props.onDestroy();
e.stopPropagation();
});
}
}
module.exports = FunctionalTab;

View File

@@ -0,0 +1,73 @@
import type {Html} from "../../../common/html.js";
import {html} from "../../../common/html.js";
import {generateNodeFromHtml} from "./base.js";
import type {TabProps} from "./tab.js";
import Tab from "./tab.js";
export type FunctionalTabProps = {
$view: Element;
} & TabProps;
export default class FunctionalTab extends Tab {
$view: Element;
$el: Element;
$closeButton?: Element;
constructor({$view, ...props}: FunctionalTabProps) {
super(props);
this.$view = $view;
this.$el = generateNodeFromHtml(this.templateHtml());
if (this.props.name !== "Settings") {
this.props.$root.append(this.$el);
this.$closeButton = this.$el.querySelector(".server-tab-badge")!;
this.registerListeners();
}
}
override async activate(): Promise<void> {
await super.activate();
this.$view.classList.add("active");
}
override async deactivate(): Promise<void> {
await super.deactivate();
this.$view.classList.remove("active");
}
override async destroy(): Promise<void> {
await super.destroy();
this.$view.remove();
}
templateHtml(): Html {
return html`
<div class="tab functional-tab" data-tab-id="${this.props.tabIndex}">
<div class="server-tab-badge close-button">
<i class="material-icons">close</i>
</div>
<div class="server-tab">
<i class="material-icons">${this.props.materialIcon}</i>
</div>
</div>
`;
}
override registerListeners(): void {
super.registerListeners();
this.$el.addEventListener("mouseover", () => {
this.$closeButton?.classList.add("active");
});
this.$el.addEventListener("mouseout", () => {
this.$closeButton?.classList.remove("active");
});
this.$closeButton?.addEventListener("click", (event: Event) => {
this.props.onDestroy?.();
event.stopPropagation();
});
}
}

View File

@@ -1,58 +0,0 @@
'use strict';
const Tab = require(__dirname + '/../components/tab.js');
const SystemUtil = require(__dirname + '/../utils/system-util.js');
const {ipcRenderer} = require('electron');
class ServerTab extends Tab {
template() {
return `<div class="tab">
<div class="server-tab-badge"></div>
<div class="server-tab">
<img class="server-icons" src='${this.props.icon}'/>
</div>
<div class="server-tab-shortcut">${this.generateShortcutText()}</div>
</div>`;
}
init() {
super.init();
this.$badge = this.$el.getElementsByClassName('server-tab-badge')[0];
}
updateBadge(count) {
if (count > 0) {
const formattedCount = count > 999 ? '1K+' : count;
this.$badge.innerHTML = formattedCount;
this.$badge.classList.add('active');
} else {
this.$badge.classList.remove('active');
}
}
generateShortcutText() {
// Only provide shortcuts for server [0..10]
if (this.props.index >= 10) {
return '';
}
const shownIndex = this.props.index + 1;
let shortcutText = '';
if (SystemUtil.getOS() === 'Mac') {
shortcutText = `${shownIndex}`;
} else {
shortcutText = `Ctrl+${shownIndex}`;
}
ipcRenderer.send('register-server-tab-shortcut', shownIndex);
return shortcutText;
}
}
module.exports = ServerTab;

View File

@@ -0,0 +1,95 @@
import process from "node:process";
import type {Html} from "../../../common/html.js";
import {html} from "../../../common/html.js";
import {ipcRenderer} from "../typed-ipc-renderer.js";
import {generateNodeFromHtml} from "./base.js";
import type {TabProps} from "./tab.js";
import Tab from "./tab.js";
import type WebView from "./webview.js";
export type ServerTabProps = {
webview: Promise<WebView>;
} & TabProps;
export default class ServerTab extends Tab {
webview: Promise<WebView>;
$el: Element;
$name: Element;
$icon: HTMLImageElement;
$badge: Element;
constructor({webview, ...props}: ServerTabProps) {
super(props);
this.webview = webview;
this.$el = generateNodeFromHtml(this.templateHtml());
this.props.$root.append(this.$el);
this.registerListeners();
this.$name = this.$el.querySelector(".server-tooltip")!;
this.$icon = this.$el.querySelector(".server-icons")!;
this.$badge = this.$el.querySelector(".server-tab-badge")!;
}
override async activate(): Promise<void> {
await super.activate();
(await this.webview).load();
}
override async deactivate(): Promise<void> {
await super.deactivate();
(await this.webview).hide();
}
override async destroy(): Promise<void> {
await super.destroy();
(await this.webview).destroy();
}
templateHtml(): Html {
return html`
<div class="tab" data-tab-id="${this.props.tabIndex}">
<div class="server-tooltip" style="display:none">
${this.props.name}
</div>
<div class="server-tab-badge"></div>
<div class="server-tab">
<img class="server-icons" src="${this.props.icon}" />
</div>
<div class="server-tab-shortcut">${this.generateShortcutText()}</div>
</div>
`;
}
setName(name: string): void {
this.props.name = name;
this.$name.textContent = name;
}
setIcon(icon: string): void {
this.props.icon = icon;
this.$icon.src = icon;
}
updateBadge(count: number): void {
this.$badge.textContent = count > 999 ? "1K+" : count.toString();
this.$badge.classList.toggle("active", count > 0);
}
generateShortcutText(): string {
// Only provide shortcuts for server [0..9]
if (this.props.index >= 9) {
return "";
}
const shownIndex = this.props.index + 1;
// Array index == Shown index - 1
ipcRenderer.send("switch-server-tab", shownIndex - 1);
return process.platform === "darwin"
? `${shownIndex}`
: `Ctrl+${shownIndex}`;
}
}

View File

@@ -1,46 +0,0 @@
'use strict';
const BaseComponent = require(__dirname + '/../components/base.js');
class Tab extends BaseComponent {
constructor(props) {
super();
this.props = props;
this.webview = this.props.webview;
this.init();
}
init() {
this.$el = this.generateNodeFromTemplate(this.template());
this.props.$root.appendChild(this.$el);
this.registerListeners();
}
registerListeners() {
this.$el.addEventListener('click', this.props.onClick);
}
isLoading() {
return this.webview.isLoading;
}
activate() {
this.$el.classList.add('active');
this.webview.load();
}
deactivate() {
this.$el.classList.remove('active');
this.webview.hide();
}
destroy() {
this.$el.parentNode.removeChild(this.$el);
this.webview.$el.parentNode.removeChild(this.webview.$el);
}
}
module.exports = Tab;

View File

@@ -0,0 +1,45 @@
import type {TabRole} from "../../../common/types.js";
export type TabProps = {
role: TabRole;
icon?: string;
name: string;
$root: Element;
onClick: () => void;
index: number;
tabIndex: number;
onHover?: () => void;
onHoverOut?: () => void;
materialIcon?: string;
onDestroy?: () => void;
};
export default abstract class Tab {
abstract $el: Element;
constructor(readonly props: TabProps) {}
registerListeners(): void {
this.$el.addEventListener("click", this.props.onClick);
if (this.props.onHover !== undefined) {
this.$el.addEventListener("mouseover", this.props.onHover);
}
if (this.props.onHoverOut !== undefined) {
this.$el.addEventListener("mouseout", this.props.onHoverOut);
}
}
async activate(): Promise<void> {
this.$el.classList.add("active");
}
async deactivate(): Promise<void> {
this.$el.classList.remove("active");
}
async destroy(): Promise<void> {
this.$el.remove();
}
}

View File

@@ -1,178 +0,0 @@
'use strict';
const path = require('path');
const fs = require('fs');
const DomainUtil = require(__dirname + '/../utils/domain-util.js');
const SystemUtil = require(__dirname + '/../utils/system-util.js');
const LinkUtil = require(__dirname + '/../utils/link-util.js');
const {shell} = require('electron').remote;
const BaseComponent = require(__dirname + '/../components/base.js');
class WebView extends BaseComponent {
constructor(props) {
super();
this.props = props;
this.zoomFactor = 1.0;
this.loading = false;
this.badgeCount = 0;
}
template() {
return `<webview
class="disabled"
src="${this.props.url}"
${this.props.nodeIntegration ? 'nodeIntegration' : ''}
disablewebsecurity
${this.props.preload ? 'preload="js/preload.js"' : ''}
webpreferences="allowRunningInsecureContent, javascript=yes">
</webview>`;
}
init() {
this.$el = this.generateNodeFromTemplate(this.template());
this.props.$root.appendChild(this.$el);
this.registerListeners();
}
registerListeners() {
this.$el.addEventListener('new-window', event => {
const {url} = event;
const domainPrefix = DomainUtil.getDomain(this.props.index).url;
if (LinkUtil.isInternal(domainPrefix, url) || url === (domainPrefix + '/')) {
event.preventDefault();
this.$el.loadURL(url);
} else {
event.preventDefault();
shell.openExternal(url);
}
});
this.$el.addEventListener('page-title-updated', event => {
const {title} = event;
this.badgeCount = this.getBadgeCount(title);
this.props.onTitleChange();
});
this.$el.addEventListener('dom-ready', () => {
this.$el.classList.add('onload');
this.show();
});
this.$el.addEventListener('did-fail-load', event => {
const {errorDescription} = event;
const hasConnectivityErr = (SystemUtil.connectivityERR.indexOf(errorDescription) >= 0);
if (hasConnectivityErr) {
console.error('error', errorDescription);
this.props.onNetworkError();
}
});
this.$el.addEventListener('did-start-loading', () => {
let userAgent = SystemUtil.getUserAgent();
if (!userAgent) {
SystemUtil.setUserAgent(this.$el.getUserAgent());
userAgent = SystemUtil.getUserAgent();
}
this.$el.setUserAgent(userAgent);
});
}
getBadgeCount(title) {
const messageCountInTitle = (/\(([0-9]+)\)/).exec(title);
return messageCountInTitle ? Number(messageCountInTitle[1]) : 0;
}
show() {
// Do not show WebView if another tab was selected and this tab should be in background.
if (!this.props.isActive()) {
return;
}
this.$el.classList.remove('disabled');
setTimeout(() => {
this.$el.classList.remove('onload');
}, 1000);
this.focus();
this.loading = false;
this.props.onTitleChange(this.$el.getTitle());
// Injecting preload css in webview to override some css rules
this.$el.insertCSS(fs.readFileSync(path.join(__dirname, '/../../css/preload.css'), 'utf8'));
}
focus() {
// focus Webview and it's contents when Window regain focus.
const webContents = this.$el.getWebContents();
if (webContents && !webContents.isFocused()) {
this.$el.focus();
webContents.focus();
}
}
hide() {
this.$el.classList.add('disabled');
}
load() {
if (this.$el) {
this.show();
} else {
this.init();
}
}
zoomIn() {
this.zoomFactor += 0.1;
this.$el.setZoomFactor(this.zoomFactor);
}
zoomOut() {
this.zoomFactor -= 0.1;
this.$el.setZoomFactor(this.zoomFactor);
}
zoomActualSize() {
this.zoomFactor = 1.0;
this.$el.setZoomFactor(this.zoomFactor);
}
logOut() {
this.$el.executeJavaScript('logout()');
}
showShortcut() {
this.$el.executeJavaScript('shortcut()');
}
openDevTools() {
this.$el.openDevTools();
}
back() {
if (this.$el.canGoBack()) {
this.$el.goBack();
}
}
forward() {
if (this.$el.canGoForward()) {
this.$el.goForward();
}
}
reload() {
this.hide();
this.$el.reload();
}
send(...param) {
this.$el.send(...param);
}
}
module.exports = WebView;

View File

@@ -0,0 +1,340 @@
import type {WebContents} from "electron/main";
import fs from "node:fs";
import process from "node:process";
import * as remote from "@electron/remote";
import {app, dialog} from "@electron/remote";
import * as ConfigUtil from "../../../common/config-util.js";
import type {Html} from "../../../common/html.js";
import {html} from "../../../common/html.js";
import type {RendererMessage} from "../../../common/typed-ipc.js";
import type {TabRole} from "../../../common/types.js";
import preloadCss from "../../css/preload.css?raw"; // eslint-disable-line n/file-extension-in-import
import {ipcRenderer} from "../typed-ipc-renderer.js";
import * as SystemUtil from "../utils/system-util.js";
import {generateNodeFromHtml} from "./base.js";
import {contextMenu} from "./context-menu.js";
const shouldSilentWebview = ConfigUtil.getConfigItem("silent", false);
type WebViewProps = {
$root: Element;
rootWebContents: WebContents;
index: number;
tabIndex: number;
url: string;
role: TabRole;
isActive: () => boolean;
switchLoading: (loading: boolean, url: string) => void;
onNetworkError: (index: number) => void;
preload?: string;
onTitleChange: () => void;
hasPermission?: (origin: string, permission: string) => boolean;
unsupportedMessage?: string;
};
export default class WebView {
static templateHtml(props: WebViewProps): Html {
return html`
<div class="webview-pane">
<div
class="webview-unsupported"
${props.unsupportedMessage === undefined ? html`hidden` : html``}
>
<span class="webview-unsupported-message"
>${props.unsupportedMessage ?? ""}</span
>
<span class="webview-unsupported-dismiss">×</span>
</div>
<webview
data-tab-id="${props.tabIndex}"
src="${props.url}"
${props.preload === undefined
? html``
: html`preload="${props.preload}"`}
partition="persist:webviewsession"
allowpopups
>
</webview>
</div>
`;
}
static async create(props: WebViewProps): Promise<WebView> {
const $pane = generateNodeFromHtml(
WebView.templateHtml(props),
) as HTMLElement;
props.$root.append($pane);
const $webview: HTMLElement = $pane.querySelector(":scope > webview")!;
await new Promise<void>((resolve) => {
$webview.addEventListener(
"did-attach",
() => {
resolve();
},
true,
);
});
// Work around https://github.com/electron/electron/issues/26904
function getWebContentsIdFunction(
this: undefined,
selector: string,
): number {
return document
.querySelector<Electron.WebviewTag>(selector)!
.getWebContentsId();
}
const selector = `webview[data-tab-id="${CSS.escape(
`${props.tabIndex}`,
)}"]`;
const webContentsId: unknown =
await props.rootWebContents.executeJavaScript(
`(${getWebContentsIdFunction.toString()})(${JSON.stringify(selector)})`,
);
if (typeof webContentsId !== "number") {
throw new TypeError("Failed to get WebContents ID");
}
return new WebView(props, $pane, $webview, webContentsId);
}
badgeCount = 0;
loading = true;
private zoomFactor = 1;
private customCss: string | false | null;
private readonly $webviewsContainer: DOMTokenList;
private readonly $unsupported: HTMLElement;
private readonly $unsupportedMessage: HTMLElement;
private readonly $unsupportedDismiss: HTMLElement;
private unsupportedDismissed = false;
private constructor(
readonly props: WebViewProps,
private readonly $pane: HTMLElement,
private readonly $webview: HTMLElement,
readonly webContentsId: number,
) {
this.customCss = ConfigUtil.getConfigItem("customCSS", null);
this.$webviewsContainer = document.querySelector(
"#webviews-container",
)!.classList;
this.$unsupported = $pane.querySelector(".webview-unsupported")!;
this.$unsupportedMessage = $pane.querySelector(
".webview-unsupported-message",
)!;
this.$unsupportedDismiss = $pane.querySelector(
".webview-unsupported-dismiss",
)!;
this.registerListeners();
}
destroy(): void {
this.$pane.remove();
}
getWebContents(): WebContents {
return remote.webContents.fromId(this.webContentsId)!;
}
showNotificationSettings(): void {
this.send("show-notification-settings");
}
focus(): void {
this.$webview.focus();
// Work around https://github.com/electron/electron/issues/31918
this.$webview.shadowRoot?.querySelector("iframe")?.focus();
}
hide(): void {
this.$pane.classList.remove("active");
}
load(): void {
this.show();
}
zoomIn(): void {
this.zoomFactor += 0.1;
this.getWebContents().setZoomFactor(this.zoomFactor);
}
zoomOut(): void {
this.zoomFactor -= 0.1;
this.getWebContents().setZoomFactor(this.zoomFactor);
}
zoomActualSize(): void {
this.zoomFactor = 1;
this.getWebContents().setZoomFactor(this.zoomFactor);
}
logOut(): void {
this.send("logout");
}
showKeyboardShortcuts(): void {
this.send("show-keyboard-shortcuts");
}
openDevTools(): void {
this.getWebContents().openDevTools();
}
back(): void {
if (this.getWebContents().canGoBack()) {
this.getWebContents().goBack();
this.focus();
}
}
canGoBackButton(): void {
const $backButton = document.querySelector(
"#actions-container #back-action",
)!;
$backButton.classList.toggle("disable", !this.getWebContents().canGoBack());
}
forward(): void {
if (this.getWebContents().canGoForward()) {
this.getWebContents().goForward();
}
}
reload(): void {
this.hide();
// Shows the loading indicator till the webview is reloaded
this.$webviewsContainer.remove("loaded");
this.loading = true;
this.props.switchLoading(true, this.props.url);
this.getWebContents().reload();
}
setUnsupportedMessage(unsupportedMessage: string | undefined) {
this.$unsupported.hidden =
unsupportedMessage === undefined || this.unsupportedDismissed;
this.$unsupportedMessage.textContent = unsupportedMessage ?? "";
}
send<Channel extends keyof RendererMessage>(
channel: Channel,
...args: Parameters<RendererMessage[Channel]>
): void {
ipcRenderer.sendTo(this.webContentsId, channel, ...args);
}
private registerListeners(): void {
const webContents = this.getWebContents();
if (shouldSilentWebview) {
webContents.setAudioMuted(true);
}
webContents.on("page-title-updated", (_event, title) => {
this.badgeCount = this.getBadgeCount(title);
this.props.onTitleChange();
});
this.$webview.addEventListener("did-navigate-in-page", () => {
this.canGoBackButton();
});
this.$webview.addEventListener("did-navigate", () => {
this.canGoBackButton();
});
webContents.on("page-favicon-updated", (_event, favicons) => {
// This returns a string of favicons URL. If there is a PM counts in unread messages then the URL would be like
// https://chat.zulip.org/static/images/favicon/favicon-pms.png
if (
favicons[0].indexOf("favicon-pms") > 0 &&
process.platform === "darwin"
) {
// This api is only supported on macOS
app.dock.setBadge("●");
// Bounce the dock
if (ConfigUtil.getConfigItem("dockBouncing", true)) {
app.dock.bounce();
}
}
});
webContents.addListener("context-menu", (event, menuParameters) => {
contextMenu(webContents, event, menuParameters);
});
this.$webview.addEventListener("dom-ready", () => {
this.loading = false;
this.props.switchLoading(false, this.props.url);
this.show();
});
webContents.on("did-fail-load", (_event, _errorCode, errorDescription) => {
const hasConnectivityError =
SystemUtil.connectivityError.includes(errorDescription);
if (hasConnectivityError) {
console.error("error", errorDescription);
if (!this.props.url.includes("network.html")) {
this.props.onNetworkError(this.props.index);
}
}
});
this.$webview.addEventListener("did-start-loading", () => {
this.props.switchLoading(true, this.props.url);
});
this.$webview.addEventListener("did-stop-loading", () => {
this.props.switchLoading(false, this.props.url);
});
this.$unsupportedDismiss.addEventListener("click", () => {
this.unsupportedDismissed = true;
this.$unsupported.hidden = true;
});
}
private getBadgeCount(title: string): number {
const messageCountInTitle = /^\((\d+)\)/.exec(title);
return messageCountInTitle ? Number(messageCountInTitle[1]) : 0;
}
private show(): void {
// Do not show WebView if another tab was selected and this tab should be in background.
if (!this.props.isActive()) {
return;
}
// To show or hide the loading indicator in the active tab
this.$webviewsContainer.toggle("loaded", !this.loading);
this.$pane.classList.add("active");
this.focus();
this.props.onTitleChange();
// Injecting preload css in webview to override some css rules
(async () => this.getWebContents().insertCSS(preloadCss))();
// Get customCSS again from config util to avoid warning user again
const customCss = ConfigUtil.getConfigItem("customCSS", null);
this.customCss = customCss;
if (customCss) {
if (!fs.existsSync(customCss)) {
this.customCss = null;
ConfigUtil.setConfigItem("customCSS", null);
const errorMessage = "The custom css previously set is deleted!";
dialog.showErrorBox("custom css file deleted!", errorMessage);
return;
}
(async () =>
this.getWebContents().insertCSS(fs.readFileSync(customCss, "utf8")))();
}
}
}

View File

@@ -0,0 +1,113 @@
import {EventEmitter} from "events"; // eslint-disable-line unicorn/prefer-node-protocol
import type {ClipboardDecrypter} from "./clipboard-decrypter.js";
import {ClipboardDecrypterImpl} from "./clipboard-decrypter.js";
import type {NotificationData} from "./notification/index.js";
import {newNotification} from "./notification/index.js";
import {ipcRenderer} from "./typed-ipc-renderer.js";
type ListenerType = (...args: any[]) => void;
/* eslint-disable @typescript-eslint/naming-convention */
export type ElectronBridge = {
send_event: (eventName: string | symbol, ...args: unknown[]) => boolean;
on_event: (eventName: string, listener: ListenerType) => void;
new_notification: (
title: string,
options: NotificationOptions,
dispatch: (type: string, eventInit: EventInit) => boolean,
) => NotificationData;
get_idle_on_system: () => boolean;
get_last_active_on_system: () => number;
get_send_notification_reply_message_supported: () => boolean;
set_send_notification_reply_message_supported: (value: boolean) => void;
decrypt_clipboard: (version: number) => ClipboardDecrypter;
};
/* eslint-enable @typescript-eslint/naming-convention */
let notificationReplySupported = false;
// Indicates if the user is idle or not
let idle = false;
// Indicates the time at which user was last active
let lastActive = Date.now();
export const bridgeEvents = new EventEmitter();
/* eslint-disable @typescript-eslint/naming-convention */
const electron_bridge: ElectronBridge = {
send_event: (eventName: string | symbol, ...args: unknown[]): boolean =>
bridgeEvents.emit(eventName, ...args),
on_event(eventName: string, listener: ListenerType): void {
bridgeEvents.on(eventName, listener);
},
new_notification: (
title: string,
options: NotificationOptions,
dispatch: (type: string, eventInit: EventInit) => boolean,
): NotificationData => newNotification(title, options, dispatch),
get_idle_on_system: (): boolean => idle,
get_last_active_on_system: (): number => lastActive,
get_send_notification_reply_message_supported: (): boolean =>
notificationReplySupported,
set_send_notification_reply_message_supported(value: boolean): void {
notificationReplySupported = value;
},
decrypt_clipboard: (version: number): ClipboardDecrypter =>
new ClipboardDecrypterImpl(version),
};
/* eslint-enable @typescript-eslint/naming-convention */
bridgeEvents.on("total_unread_count", (unreadCount: unknown) => {
if (typeof unreadCount !== "number") {
throw new TypeError("Expected string for unreadCount");
}
ipcRenderer.send("unread-count", unreadCount);
});
bridgeEvents.on("realm_name", (realmName: unknown) => {
if (typeof realmName !== "string") {
throw new TypeError("Expected string for realmName");
}
const serverUrl = location.origin;
ipcRenderer.send("realm-name-changed", serverUrl, realmName);
});
bridgeEvents.on("realm_icon_url", (iconUrl: unknown) => {
if (typeof iconUrl !== "string") {
throw new TypeError("Expected string for iconUrl");
}
const serverUrl = location.origin;
ipcRenderer.send(
"realm-icon-changed",
serverUrl,
iconUrl.includes("http") ? iconUrl : `${serverUrl}${iconUrl}`,
);
});
// Set user as active and update the time of last activity
ipcRenderer.on("set-active", () => {
idle = false;
lastActive = Date.now();
});
// Set user as idle and time of last activity is left unchanged
ipcRenderer.on("set-idle", () => {
idle = true;
});
// This follows node's idiomatic implementation of event
// emitters to make event handling more simpler instead of using
// functions zulip side will emit event using ElectronBridge.send_event
// which is alias of .emit and on this side we can handle the data by adding
// a listener for the event.
export default electron_bridge;

View File

@@ -1,334 +0,0 @@
'use strict';
require(__dirname + '/js/tray.js');
const {ipcRenderer} = require('electron');
const DomainUtil = require(__dirname + '/js/utils/domain-util.js');
const WebView = require(__dirname + '/js/components/webview.js');
const ServerTab = require(__dirname + '/js/components/server-tab.js');
const FunctionalTab = require(__dirname + '/js/components/functional-tab.js');
const ConfigUtil = require(__dirname + '/js/utils/config-util.js');
class ServerManagerView {
constructor() {
this.$addServerButton = document.getElementById('add-tab');
this.$tabsContainer = document.getElementById('tabs-container');
const $actionsContainer = document.getElementById('actions-container');
this.$reloadButton = $actionsContainer.querySelector('#reload-action');
this.$settingsButton = $actionsContainer.querySelector('#settings-action');
this.$webviewsContainer = document.getElementById('webviews-container');
this.$reloadTooltip = $actionsContainer.querySelector('#reload-tooltip');
this.$settingsTooltip = $actionsContainer.querySelector('#setting-tooltip');
this.$sidebar = document.getElementById('sidebar');
this.$fullscreenPopup = document.getElementById('fullscreen-popup');
this.$fullscreenEscapeKey = process.platform === 'darwin' ? '^⌘F' : 'F11';
this.$fullscreenPopup.innerHTML = `Press ${this.$fullscreenEscapeKey} to exit full screen`;
this.activeTabIndex = -1;
this.tabs = [];
this.functionalTabs = {};
}
init() {
this.initSidebar();
this.initTabs();
this.initActions();
this.registerIpcs();
}
initSidebar() {
const showSidebar = ConfigUtil.getConfigItem('show-sidebar', true);
this.toggleSidebar(showSidebar);
}
initTabs() {
const servers = DomainUtil.getDomains();
if (servers.length > 0) {
for (let i = 0; i < servers.length; i++) {
this.initServer(servers[i], i);
}
this.activateTab(0);
} else {
this.openSettings('Servers');
}
ipcRenderer.send('local-shortcuts', true);
}
initServer(server, index) {
this.tabs.push(new ServerTab({
role: 'server',
icon: server.icon,
$root: this.$tabsContainer,
onClick: this.activateTab.bind(this, index),
index,
webview: new WebView({
$root: this.$webviewsContainer,
index,
url: server.url,
name: server.alias,
isActive: () => {
return index === this.activeTabIndex;
},
onNetworkError: this.openNetworkTroubleshooting.bind(this),
onTitleChange: this.updateBadge.bind(this),
nodeIntegration: false,
preload: true
})
}));
}
initActions() {
this.$reloadButton.addEventListener('click', () => {
this.tabs[this.activeTabIndex].webview.reload();
});
this.$addServerButton.addEventListener('click', () => {
this.openSettings('Servers');
});
this.$settingsButton.addEventListener('click', () => {
this.openSettings('General');
});
this.$reloadButton.addEventListener('mouseover', () => {
this.$reloadTooltip.removeAttribute('style');
});
this.$reloadButton.addEventListener('mouseout', () => {
this.$reloadTooltip.style.display = 'none';
});
this.$settingsButton.addEventListener('mouseover', () => {
this.$settingsTooltip.removeAttribute('style');
});
this.$settingsButton.addEventListener('mouseout', () => {
this.$settingsTooltip.style.display = 'none';
});
}
openFunctionalTab(tabProps) {
if (this.functionalTabs[tabProps.name] !== undefined) {
this.activateTab(this.functionalTabs[tabProps.name]);
return;
}
this.functionalTabs[tabProps.name] = this.tabs.length;
this.tabs.push(new FunctionalTab({
role: 'function',
materialIcon: tabProps.materialIcon,
$root: this.$tabsContainer,
index: this.functionalTabs[tabProps.name],
onClick: this.activateTab.bind(this, this.functionalTabs[tabProps.name]),
onDestroy: this.destroyTab.bind(this, tabProps.name, this.functionalTabs[tabProps.name]),
webview: new WebView({
$root: this.$webviewsContainer,
index: this.functionalTabs[tabProps.name],
url: tabProps.url,
name: tabProps.name,
isActive: () => {
return this.functionalTabs[tabProps.name] === this.activeTabIndex;
},
onNetworkError: this.openNetworkTroubleshooting.bind(this),
onTitleChange: this.updateBadge.bind(this),
nodeIntegration: true,
preload: false
})
}));
this.activateTab(this.functionalTabs[tabProps.name]);
}
openSettings(nav = 'General') {
this.openFunctionalTab({
name: 'Settings',
materialIcon: 'settings',
url: `file://${__dirname}/preference.html#${nav}`
});
this.tabs[this.functionalTabs.Settings].webview.send('switch-settings-nav', nav);
}
openAbout() {
this.openFunctionalTab({
name: 'About',
materialIcon: 'sentiment_very_satisfied',
url: `file://${__dirname}/about.html`
});
}
openNetworkTroubleshooting() {
this.openFunctionalTab({
name: 'Network Troubleshooting',
materialIcon: 'network_check',
url: `file://${__dirname}/network.html`
});
}
activateTab(index, hideOldTab = true) {
if (this.tabs[index].webview.loading) {
return;
}
if (this.activeTabIndex !== -1) {
if (this.activeTabIndex === index) {
return;
} else if (hideOldTab) {
this.tabs[this.activeTabIndex].deactivate();
}
}
this.activeTabIndex = index;
this.tabs[index].activate();
ipcRenderer.send('update-menu', {
tabs: this.tabs,
activeTabIndex: this.activeTabIndex
});
}
destroyTab(name, index) {
if (this.tabs[index].webview.loading) {
return;
}
this.tabs[index].destroy();
delete this.tabs[index];
delete this.functionalTabs[name];
// Issue #188: If the functional tab was not focused, do not activate another tab.
if (this.activeTabIndex === index) {
this.activateTab(0, false);
}
}
destroyView() {
// Clear global variables
this.activeTabIndex = -1;
this.tabs = [];
this.functionalTabs = {};
// Clear DOM elements
this.$tabsContainer.innerHTML = '';
this.$webviewsContainer.innerHTML = '';
// Destroy shortcuts
ipcRenderer.send('local-shortcuts', false);
}
reloadView() {
this.destroyView();
this.initTabs();
}
updateBadge() {
let messageCountAll = 0;
for (let i = 0; i < this.tabs.length; i++) {
if (this.tabs[i] && this.tabs[i].updateBadge) {
const count = this.tabs[i].webview.badgeCount;
messageCountAll += count;
this.tabs[i].updateBadge(count);
}
}
ipcRenderer.send('update-badge', messageCountAll);
}
toggleSidebar(show) {
if (show) {
this.$sidebar.classList.remove('hidden');
} else {
this.$sidebar.classList.add('hidden');
}
}
registerIpcs() {
const webviewListeners = {
'webview-reload': 'reload',
back: 'back',
focus: 'focus',
forward: 'forward',
zoomIn: 'zoomIn',
zoomOut: 'zoomOut',
zoomActualSize: 'zoomActualSize',
'log-out': 'logOut',
shortcut: 'showShortcut',
'tab-devtools': 'openDevTools'
};
for (const key in webviewListeners) {
ipcRenderer.on(key, () => {
const activeWebview = this.tabs[this.activeTabIndex].webview;
if (activeWebview) {
activeWebview[webviewListeners[key]]();
}
});
}
ipcRenderer.on('open-settings', (event, settingNav) => {
this.openSettings(settingNav);
});
ipcRenderer.on('open-about', this.openAbout.bind(this));
ipcRenderer.on('reload-viewer', this.reloadView.bind(this));
ipcRenderer.on('hard-reload', () => {
ipcRenderer.send('reload-full-app');
});
ipcRenderer.on('switch-server-tab', (event, index) => {
this.activateTab(index);
});
ipcRenderer.on('toggle-sidebar', (event, show) => {
this.toggleSidebar(show);
});
ipcRenderer.on('enter-fullscreen', () => {
this.$fullscreenPopup.classList.add('show');
this.$fullscreenPopup.classList.remove('hidden');
});
ipcRenderer.on('leave-fullscreen', () => {
this.$fullscreenPopup.classList.remove('show');
});
ipcRenderer.on('render-taskbar-icon', (event, messageCount) => {
// Create a canvas from unread messagecounts
function createOverlayIcon(messageCount) {
const canvas = document.createElement('canvas');
canvas.height = 128;
canvas.width = 128;
canvas.style.letterSpacing = '-5px';
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#f42020';
ctx.beginPath();
ctx.ellipse(64, 64, 64, 64, 0, 0, 2 * Math.PI);
ctx.fill();
ctx.textAlign = 'center';
ctx.fillStyle = 'white';
if (messageCount > 99) {
ctx.font = '65px Helvetica';
ctx.fillText('99+', 64, 85);
} else if (messageCount < 10) {
ctx.font = '90px Helvetica';
ctx.fillText(String(Math.min(99, messageCount)), 64, 96);
} else {
ctx.font = '85px Helvetica';
ctx.fillText(String(Math.min(99, messageCount)), 64, 90);
}
return canvas;
}
ipcRenderer.send('update-taskbar-icon', createOverlayIcon(messageCount).toDataURL(), String(messageCount));
});
}
}
window.onload = () => {
const serverManagerView = new ServerManagerView();
serverManagerView.init();
window.addEventListener('online', () => {
serverManagerView.reloadView();
});
};

1187
app/renderer/js/main.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +0,0 @@
'use strict';
const {remote} = require('electron');
const ConfigUtil = require(__dirname + '/utils/config-util.js');
const app = remote.app;
// From https://github.com/felixrieseberg/electron-windows-notifications#appusermodelid
// On windows 8 we have to explicitly set the appUserModelId otherwise notification won't work.
app.setAppUserModelId('org.zulip.zulip-electron');
const NativeNotification = window.Notification;
class SilentNotification extends NativeNotification {
constructor(title, opts) {
opts.silent = ConfigUtil.getConfigItem('silent') || false;
super(title, opts);
}
}
window.Notification = SilentNotification;

View File

@@ -0,0 +1,41 @@
import {ipcRenderer} from "../typed-ipc-renderer.js";
export type NotificationData = {
close: () => void;
title: string;
dir: NotificationDirection;
lang: string;
body: string;
tag: string;
icon: string;
data: unknown;
};
export function newNotification(
title: string,
options: NotificationOptions,
dispatch: (type: string, eventInit: EventInit) => boolean,
): NotificationData {
const notification = new Notification(title, {...options, silent: true});
for (const type of ["click", "close", "error", "show"]) {
notification.addEventListener(type, (ev: Event) => {
if (type === "click") ipcRenderer.send("focus-this-webview");
if (!dispatch(type, ev)) {
ev.preventDefault();
}
});
}
return {
close() {
notification.close();
},
title: notification.title,
dir: notification.dir,
lang: notification.lang,
body: notification.body,
tag: notification.tag,
icon: notification.icon,
data: notification.data,
};
}

View File

@@ -0,0 +1,24 @@
import {app} from "@electron/remote";
import {bundleUrl} from "../../../common/paths.js";
export class AboutView {
static async create(): Promise<AboutView> {
return new AboutView(
await (await fetch(new URL("app/renderer/about.html", bundleUrl))).text(),
);
}
readonly $view: HTMLElement;
private constructor(templateHtml: string) {
this.$view = document.createElement("div");
const $shadow = this.$view.attachShadow({mode: "open"});
$shadow.innerHTML = templateHtml;
$shadow.querySelector("#version")!.textContent = `v${app.getVersion()}`;
}
destroy() {
// Do nothing.
}
}

View File

@@ -1,20 +0,0 @@
'use strict';
const {ipcRenderer} = require('electron');
class NetworkTroubleshootingView {
constructor() {
this.$reconnectButton = document.getElementById('reconnect');
}
init() {
this.$reconnectButton.addEventListener('click', () => {
ipcRenderer.send('forward-message', 'reload-viewer');
});
}
}
window.onload = () => {
const networkTroubleshootingView = new NetworkTroubleshootingView();
networkTroubleshootingView.init();
};

View File

@@ -0,0 +1,13 @@
import {ipcRenderer} from "../typed-ipc-renderer.js";
export function init(
$reconnectButton: Element,
$settingsButton: Element,
): void {
$reconnectButton.addEventListener("click", () => {
ipcRenderer.send("forward-message", "reload-viewer");
});
$settingsButton.addEventListener("click", () => {
ipcRenderer.send("forward-message", "open-settings");
});
}

View File

@@ -0,0 +1,84 @@
import type {Html} from "../../../../common/html.js";
import {html} from "../../../../common/html.js";
import {generateNodeFromHtml} from "../../components/base.js";
import {ipcRenderer} from "../../typed-ipc-renderer.js";
type BaseSectionProps = {
$element: HTMLElement;
disabled?: boolean;
value: boolean;
clickHandler: () => void;
};
export function generateSettingOption(props: BaseSectionProps): void {
const {$element, disabled, value, clickHandler} = props;
$element.textContent = "";
const $optionControl = generateNodeFromHtml(
generateOptionHtml(value, disabled),
);
$element.append($optionControl);
if (!disabled) {
$optionControl.addEventListener("click", clickHandler);
}
}
export function generateOptionHtml(
settingOption: boolean,
disabled?: boolean,
): Html {
const labelHtml = disabled
? // eslint-disable-next-line unicorn/template-indent
html`<label
class="disallowed"
title="Setting locked by system administrator."
></label>`
: html`<label></label>`;
if (settingOption) {
return html`
<div class="action">
<div class="switch">
<input class="toggle toggle-round" type="checkbox" checked disabled />
${labelHtml}
</div>
</div>
`;
}
return html`
<div class="action">
<div class="switch">
<input class="toggle toggle-round" type="checkbox" />
${labelHtml}
</div>
</div>
`;
}
/* A method that in future can be used to create dropdown menus using <select> <option> tags.
it needs an object which has ``key: value`` pairs and will return a string that can be appended to HTML
*/
export function generateSelectHtml(
options: Record<string, string>,
className?: string,
idName?: string,
): Html {
const optionsHtml = html``.join(
Object.keys(options).map(
(key) => html`
<option name="${key}" value="${key}">${options[key]}</option>
`,
),
);
return html`
<select class="${className}" id="${idName}">
${optionsHtml}
</select>
`;
}
export function reloadApp(): void {
ipcRenderer.send("forward-message", "reload-viewer");
}

View File

@@ -0,0 +1,67 @@
import {html} from "../../../../common/html.js";
import * as t from "../../../../common/translation-util.js";
import {ipcRenderer} from "../../typed-ipc-renderer.js";
import * as DomainUtil from "../../utils/domain-util.js";
import {reloadApp} from "./base-section.js";
import {initFindAccounts} from "./find-accounts.js";
import {initServerInfoForm} from "./server-info-form.js";
type ConnectedOrgSectionProps = {
$root: Element;
};
export function initConnectedOrgSection({
$root,
}: ConnectedOrgSectionProps): void {
$root.textContent = "";
const servers = DomainUtil.getDomains();
$root.innerHTML = html`
<div class="settings-pane" id="server-settings-pane">
<div class="page-title">${t.__("Connected organizations")}</div>
<div class="title" id="existing-servers">
${t.__("All the connected organizations will appear here.")}
</div>
<div id="server-info-container"></div>
<div id="new-org-button">
<button class="green sea w-250">
${t.__("Connect to another organization")}
</button>
</div>
<div class="page-title">${t.__("Find accounts by email")}</div>
<div id="find-accounts-container"></div>
</div>
`.html;
const $serverInfoContainer = $root.querySelector("#server-info-container")!;
const $existingServers = $root.querySelector("#existing-servers")!;
const $newOrgButton: HTMLButtonElement =
$root.querySelector("#new-org-button")!;
const $findAccountsContainer = $root.querySelector(
"#find-accounts-container",
)!;
const noServerText = t.__(
"All the connected organizations will appear here.",
);
// Show noServerText if no servers are there otherwise hide it
$existingServers.textContent = servers.length === 0 ? noServerText : "";
for (const [i, server] of servers.entries()) {
initServerInfoForm({
$root: $serverInfoContainer,
server,
index: i,
onChange: reloadApp,
});
}
$newOrgButton.addEventListener("click", () => {
ipcRenderer.send("forward-message", "open-org-tab");
});
initFindAccounts({
$root: $findAccountsContainer,
});
}

View File

@@ -0,0 +1,66 @@
import {html} from "../../../../common/html.js";
import * as LinkUtil from "../../../../common/link-util.js";
import * as t from "../../../../common/translation-util.js";
import {generateNodeFromHtml} from "../../components/base.js";
type FindAccountsProps = {
$root: Element;
};
async function findAccounts(url: string): Promise<void> {
if (!url) {
return;
}
if (!url.startsWith("http")) {
url = "https://" + url;
}
await LinkUtil.openBrowser(new URL("/accounts/find", url));
}
export function initFindAccounts(props: FindAccountsProps): void {
const $findAccounts = generateNodeFromHtml(html`
<div class="settings-card certificate-card">
<div class="certificate-input">
<div>${t.__("Organization URL")}</div>
<input class="setting-input-value" value="zulipchat.com" />
</div>
<div class="certificate-input">
<button class="green w-150" id="find-accounts-button">
${t.__("Find accounts")}
</button>
</div>
</div>
`);
props.$root.append($findAccounts);
const $findAccountsButton = $findAccounts.querySelector(
"#find-accounts-button",
)!;
const $serverUrlField: HTMLInputElement = $findAccounts.querySelector(
"input.setting-input-value",
)!;
$findAccountsButton.addEventListener("click", async () => {
await findAccounts($serverUrlField.value);
});
$serverUrlField.addEventListener("click", () => {
if ($serverUrlField.value === "zulipchat.com") {
$serverUrlField.setSelectionRange(0, 0);
}
});
$serverUrlField.addEventListener("keypress", async (event) => {
if (event.key === "Enter") {
await findAccounts($serverUrlField.value);
}
});
$serverUrlField.addEventListener("input", () => {
$serverUrlField.classList.toggle(
"invalid-input-value",
$serverUrlField.value === "",
);
});
}

View File

@@ -1,156 +0,0 @@
'use strict';
const {ipcRenderer} = require('electron');
const BaseComponent = require(__dirname + '/../../components/base.js');
const ConfigUtil = require(__dirname + '/../../utils/config-util.js');
class GeneralSection extends BaseComponent {
constructor(props) {
super();
this.props = props;
}
template() {
return `
<div class="settings-pane" id="server-settings-pane">
<div class="title">Tray Options</div>
<div id="tray-option-settings" class="settings-card">
<div class="setting-row">
<div class="setting-description">Show app icon in system tray</div>
<div class="setting-control"></div>
</div>
</div>
<div class="title">App Updates</div>
<div id="betaupdate-option-settings" class="settings-card">
<div class="setting-row">
<div class="setting-description">Get beta updates</div>
<div class="setting-control"></div>
</div>
</div>
<div class="title">Desktop Notification</div>
<div id="silent-option-settings" class="settings-card">
<div class="setting-row">
<div class="setting-description">Mute all sounds from Zulip (requires reload)</div>
<div class="setting-control"></div>
</div>
</div>
<div class="title">User Interface</div>
<div id="ui-option-settings" class="settings-card">
<div class="setting-row" id="sidebar-option">
<div class="setting-description">Show sidebar (<span class="code">CmdOrCtrl+S</span>)</div>
<div class="setting-control"></div>
</div>
</div>
</div>
`;
}
settingsOptionTemplate(settingOption) {
if (settingOption) {
return `
<div class="action green">
<span>On</span>
</div>
`;
} else {
return `
<div class="action red">
<span>Off</span>
</div>
`;
}
}
trayOptionTemplate(trayOption) {
this.settingsOptionTemplate(trayOption);
}
updateOptionTemplate(updateOption) {
this.settingsOptionTemplate(updateOption);
}
silentOptionTemplate(silentOption) {
this.settingsOptionTemplate(silentOption);
}
sidebarToggleTemplate(toggleOption) {
this.settingsOptionTemplate(toggleOption);
}
init() {
this.props.$root.innerHTML = this.template();
this.initTrayOption();
this.initUpdateOption();
this.initSilentOption();
this.initSidebarToggle();
}
initTrayOption() {
this.$trayOptionSettings = document.querySelector('#tray-option-settings .setting-control');
this.$trayOptionSettings.innerHTML = '';
const trayOption = ConfigUtil.getConfigItem('trayIcon', true);
const $trayOption = this.generateNodeFromTemplate(this.settingsOptionTemplate(trayOption));
this.$trayOptionSettings.appendChild($trayOption);
$trayOption.addEventListener('click', () => {
const newValue = !ConfigUtil.getConfigItem('trayIcon');
ConfigUtil.setConfigItem('trayIcon', newValue);
ipcRenderer.send('forward-message', 'toggletray');
this.initTrayOption();
});
}
initUpdateOption() {
this.$updateOptionSettings = document.querySelector('#betaupdate-option-settings .setting-control');
this.$updateOptionSettings.innerHTML = '';
const updateOption = ConfigUtil.getConfigItem('betaUpdate', false);
const $updateOption = this.generateNodeFromTemplate(this.settingsOptionTemplate(updateOption));
this.$updateOptionSettings.appendChild($updateOption);
$updateOption.addEventListener('click', () => {
const newValue = !ConfigUtil.getConfigItem('betaUpdate');
ConfigUtil.setConfigItem('betaUpdate', newValue);
this.initUpdateOption();
});
}
initSilentOption() {
this.$silentOptionSettings = document.querySelector('#silent-option-settings .setting-control');
this.$silentOptionSettings.innerHTML = '';
const silentOption = ConfigUtil.getConfigItem('silent', false);
const $silentOption = this.generateNodeFromTemplate(this.settingsOptionTemplate(silentOption));
this.$silentOptionSettings.appendChild($silentOption);
$silentOption.addEventListener('click', () => {
const newValue = !ConfigUtil.getConfigItem('silent', true);
ConfigUtil.setConfigItem('silent', newValue);
this.initSilentOption();
});
}
initSidebarToggle() {
this.$sidebarOptionSettings = document.querySelector('#ui-option-settings #sidebar-option .setting-control');
this.$sidebarOptionSettings.innerHTML = '';
const sidebarOption = ConfigUtil.getConfigItem('show-sidebar', true);
const $sidebarOption = this.generateNodeFromTemplate(this.settingsOptionTemplate(sidebarOption));
this.$sidebarOptionSettings.appendChild($sidebarOption);
$sidebarOption.addEventListener('click', () => {
const newValue = !ConfigUtil.getConfigItem('show-sidebar');
ConfigUtil.setConfigItem('show-sidebar', newValue);
ipcRenderer.send('forward-message', 'toggle-sidebar', newValue);
this.initSidebarToggle();
});
}
handleServerInfoChange() {
ipcRenderer.send('forward-message', 'reload-viewer');
}
}
module.exports = GeneralSection;

View File

@@ -0,0 +1,705 @@
import type {OpenDialogOptions} from "electron/renderer";
import fs from "node:fs";
import path from "node:path";
import process from "node:process";
import * as remote from "@electron/remote";
import {app, dialog, session} from "@electron/remote";
import Tagify from "@yaireo/tagify";
import ISO6391 from "iso-639-1";
import {z} from "zod";
import supportedLocales from "../../../../../public/translations/supported-locales.json";
import * as ConfigUtil from "../../../../common/config-util.js";
import * as EnterpriseUtil from "../../../../common/enterprise-util.js";
import {html} from "../../../../common/html.js";
import * as t from "../../../../common/translation-util.js";
import {ipcRenderer} from "../../typed-ipc-renderer.js";
import {generateSelectHtml, generateSettingOption} from "./base-section.js";
const currentBrowserWindow = remote.getCurrentWindow();
type GeneralSectionProps = {
$root: Element;
};
export function initGeneralSection({$root}: GeneralSectionProps): void {
$root.innerHTML = html`
<div class="settings-pane">
<div class="title">${t.__("Appearance")}</div>
<div id="appearance-option-settings" class="settings-card">
<div class="setting-row" id="tray-option">
<div class="setting-description">
${t.__("Show app icon in system tray")}
</div>
<div class="setting-control"></div>
</div>
<div
class="setting-row"
id="menubar-option"
style="display:${process.platform === "darwin" ? "none" : ""}"
>
<div class="setting-description">
${t.__("Auto hide menu bar (Press Alt key to display)")}
</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="sidebar-option">
<div class="setting-description">
${t.__("Show sidebar")} (<span class="code"
>${process.platform === "darwin"
? "Cmd+Shift+S"
: "Ctrl+Shift+S"}</span
>)
</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="badge-option">
<div class="setting-description">
${t.__("Show app unread badge")}
</div>
<div class="setting-control"></div>
</div>
<div
class="setting-row"
id="dock-bounce-option"
style="display:${process.platform === "darwin" ? "" : "none"}"
>
<div class="setting-description">
${t.__("Bounce dock on new private message")}
</div>
<div class="setting-control"></div>
</div>
<div
class="setting-row"
id="flash-taskbar-option"
style="display:${process.platform === "win32" ? "" : "none"}"
>
<div class="setting-description">
${t.__("Flash taskbar on new message")}
</div>
<div class="setting-control"></div>
</div>
</div>
<div class="title">${t.__("Desktop Notifications")}</div>
<div class="settings-card">
<div class="setting-row" id="show-notification-option">
<div class="setting-description">
${t.__("Show desktop notifications")}
</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="silent-option">
<div class="setting-description">
${t.__("Mute all sounds from Zulip")}
</div>
<div class="setting-control"></div>
</div>
</div>
<div class="title">${t.__("App Updates")}</div>
<div class="settings-card">
<div class="setting-row" id="autoupdate-option">
<div class="setting-description">${t.__("Enable auto updates")}</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="betaupdate-option">
<div class="setting-description">${t.__("Get beta updates")}</div>
<div class="setting-control"></div>
</div>
</div>
<div class="title">${t.__("Functionality")}</div>
<div class="settings-card">
<div class="setting-row" id="startAtLogin-option">
<div class="setting-description">${t.__("Start app at login")}</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="start-minimize-option">
<div class="setting-description">
${t.__("Always start minimized")}
</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="quitOnClose-option">
<div class="setting-description">
${t.__("Quit when the window is closed")}
</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="enable-spellchecker-option">
<div class="setting-description">
${t.__("Enable spellchecker (requires restart)")}
</div>
<div class="setting-control"></div>
</div>
<div
class="setting-row"
id="spellcheck-langs"
style="display:${process.platform === "darwin" ? "none" : ""}"
></div>
<div class="setting-row" id="note"></div>
</div>
<div class="title">${t.__("Advanced")}</div>
<div class="settings-card">
<div class="setting-row" id="enable-error-reporting">
<div class="setting-description">
${t.__("Enable error reporting (requires restart)")}
</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="app-language">
<div class="setting-description">
${t.__("App language (requires restart)")}
</div>
<div id="lang-div" class="lang-div"></div>
</div>
<div class="setting-row" id="add-custom-css">
<div class="setting-description">${t.__("Add custom CSS")}</div>
<button class="custom-css-button green">${t.__("Upload")}</button>
</div>
<div class="setting-row" id="remove-custom-css">
<div class="setting-description">
<div class="selected-css-path" id="custom-css-path">
${ConfigUtil.getConfigItem("customCSS", "")}
</div>
</div>
<div class="action red" id="css-delete-action">
<i class="material-icons">indeterminate_check_box</i>
<span>${t.__("Delete")}</span>
</div>
</div>
<div class="setting-row" id="download-folder">
<div class="setting-description">
${t.__("Default download location")}
</div>
<button class="download-folder-button green">
${t.__("Change")}
</button>
</div>
<div class="setting-row">
<div class="setting-description">
<div class="download-folder-path">
${ConfigUtil.getConfigItem(
"downloadsPath",
app.getPath("downloads"),
)}
</div>
</div>
</div>
<div class="setting-row" id="prompt-download">
<div class="setting-description">
${t.__("Ask where to save files before downloading")}
</div>
<div class="setting-control"></div>
</div>
</div>
<div class="title">${t.__("Factory Reset Data")}</div>
<div class="settings-card">
<div class="setting-row" id="factory-reset-option">
<div class="setting-description">
${t.__(
"Reset the application, thus deleting all the connected organizations and accounts.",
)}
</div>
<button class="factory-reset-button red w-150">
${t.__("Factory Reset")}
</button>
</div>
</div>
</div>
`.html;
updateTrayOption();
updateBadgeOption();
updateSilentOption();
autoUpdateOption();
betaUpdateOption();
updateSidebarOption();
updateStartAtLoginOption();
factoryReset();
showDesktopNotification();
enableSpellchecker();
minimizeOnStart();
addCustomCss();
showCustomCssPath();
removeCustomCss();
downloadFolder();
updateQuitOnCloseOption();
updatePromptDownloadOption();
enableErrorReporting();
setLocale();
initSpellChecker();
// Platform specific settings
// Flashing taskbar on Windows
if (process.platform === "win32") {
updateFlashTaskbar();
}
// Dock bounce on macOS
if (process.platform === "darwin") {
updateDockBouncing();
}
// Auto hide menubar on Windows and Linux
if (process.platform !== "darwin") {
updateMenubarOption();
}
function updateTrayOption(): void {
generateSettingOption({
$element: $root.querySelector("#tray-option .setting-control")!,
value: ConfigUtil.getConfigItem("trayIcon", true),
clickHandler() {
const newValue = !ConfigUtil.getConfigItem("trayIcon", true);
ConfigUtil.setConfigItem("trayIcon", newValue);
ipcRenderer.send("forward-message", "toggletray");
updateTrayOption();
},
});
}
function updateMenubarOption(): void {
generateSettingOption({
$element: $root.querySelector("#menubar-option .setting-control")!,
value: ConfigUtil.getConfigItem("autoHideMenubar", false),
clickHandler() {
const newValue = !ConfigUtil.getConfigItem("autoHideMenubar", false);
ConfigUtil.setConfigItem("autoHideMenubar", newValue);
ipcRenderer.send("toggle-menubar", newValue);
updateMenubarOption();
},
});
}
function updateBadgeOption(): void {
generateSettingOption({
$element: $root.querySelector("#badge-option .setting-control")!,
value: ConfigUtil.getConfigItem("badgeOption", true),
clickHandler() {
const newValue = !ConfigUtil.getConfigItem("badgeOption", true);
ConfigUtil.setConfigItem("badgeOption", newValue);
ipcRenderer.send("toggle-badge-option", newValue);
updateBadgeOption();
},
});
}
function updateDockBouncing(): void {
generateSettingOption({
$element: $root.querySelector("#dock-bounce-option .setting-control")!,
value: ConfigUtil.getConfigItem("dockBouncing", true),
clickHandler() {
const newValue = !ConfigUtil.getConfigItem("dockBouncing", true);
ConfigUtil.setConfigItem("dockBouncing", newValue);
updateDockBouncing();
},
});
}
function updateFlashTaskbar(): void {
generateSettingOption({
$element: $root.querySelector("#flash-taskbar-option .setting-control")!,
value: ConfigUtil.getConfigItem("flashTaskbarOnMessage", true),
clickHandler() {
const newValue = !ConfigUtil.getConfigItem(
"flashTaskbarOnMessage",
true,
);
ConfigUtil.setConfigItem("flashTaskbarOnMessage", newValue);
updateFlashTaskbar();
},
});
}
function autoUpdateOption(): void {
generateSettingOption({
$element: $root.querySelector("#autoupdate-option .setting-control")!,
disabled: EnterpriseUtil.configItemExists("autoUpdate"),
value: ConfigUtil.getConfigItem("autoUpdate", true),
clickHandler() {
const newValue = !ConfigUtil.getConfigItem("autoUpdate", true);
ConfigUtil.setConfigItem("autoUpdate", newValue);
if (!newValue) {
ConfigUtil.setConfigItem("betaUpdate", false);
betaUpdateOption();
}
autoUpdateOption();
},
});
}
function betaUpdateOption(): void {
generateSettingOption({
$element: $root.querySelector("#betaupdate-option .setting-control")!,
value: ConfigUtil.getConfigItem("betaUpdate", false),
clickHandler() {
const newValue = !ConfigUtil.getConfigItem("betaUpdate", false);
if (ConfigUtil.getConfigItem("autoUpdate", true)) {
ConfigUtil.setConfigItem("betaUpdate", newValue);
betaUpdateOption();
}
},
});
}
function updateSilentOption(): void {
generateSettingOption({
$element: $root.querySelector("#silent-option .setting-control")!,
value: ConfigUtil.getConfigItem("silent", false),
clickHandler() {
const newValue = !ConfigUtil.getConfigItem("silent", true);
ConfigUtil.setConfigItem("silent", newValue);
updateSilentOption();
ipcRenderer.sendTo(
currentBrowserWindow.webContents.id,
"toggle-silent",
newValue,
);
},
});
}
function showDesktopNotification(): void {
generateSettingOption({
$element: $root.querySelector(
"#show-notification-option .setting-control",
)!,
value: ConfigUtil.getConfigItem("showNotification", true),
clickHandler() {
const newValue = !ConfigUtil.getConfigItem("showNotification", true);
ConfigUtil.setConfigItem("showNotification", newValue);
showDesktopNotification();
},
});
}
function updateSidebarOption(): void {
generateSettingOption({
$element: $root.querySelector("#sidebar-option .setting-control")!,
value: ConfigUtil.getConfigItem("showSidebar", true),
clickHandler() {
const newValue = !ConfigUtil.getConfigItem("showSidebar", true);
ConfigUtil.setConfigItem("showSidebar", newValue);
ipcRenderer.send("forward-message", "toggle-sidebar", newValue);
updateSidebarOption();
},
});
}
function updateStartAtLoginOption(): void {
generateSettingOption({
$element: $root.querySelector("#startAtLogin-option .setting-control")!,
value: ConfigUtil.getConfigItem("startAtLogin", false),
clickHandler() {
const newValue = !ConfigUtil.getConfigItem("startAtLogin", false);
ConfigUtil.setConfigItem("startAtLogin", newValue);
ipcRenderer.send("toggleAutoLauncher", newValue);
updateStartAtLoginOption();
},
});
}
function updateQuitOnCloseOption(): void {
generateSettingOption({
$element: $root.querySelector("#quitOnClose-option .setting-control")!,
value: ConfigUtil.getConfigItem("quitOnClose", false),
clickHandler() {
const newValue = !ConfigUtil.getConfigItem("quitOnClose", false);
ConfigUtil.setConfigItem("quitOnClose", newValue);
updateQuitOnCloseOption();
},
});
}
function enableSpellchecker(): void {
generateSettingOption({
$element: $root.querySelector(
"#enable-spellchecker-option .setting-control",
)!,
value: ConfigUtil.getConfigItem("enableSpellchecker", true),
clickHandler() {
const newValue = !ConfigUtil.getConfigItem("enableSpellchecker", true);
ConfigUtil.setConfigItem("enableSpellchecker", newValue);
ipcRenderer.send("configure-spell-checker");
enableSpellchecker();
const spellcheckerLanguageInput: HTMLElement =
$root.querySelector("#spellcheck-langs")!;
const spellcheckerNote: HTMLElement = $root.querySelector("#note")!;
spellcheckerLanguageInput.style.display =
spellcheckerLanguageInput.style.display === "none" ? "" : "none";
spellcheckerNote.style.display =
spellcheckerNote.style.display === "none" ? "" : "none";
},
});
}
function enableErrorReporting(): void {
generateSettingOption({
$element: $root.querySelector(
"#enable-error-reporting .setting-control",
)!,
value: ConfigUtil.getConfigItem("errorReporting", true),
clickHandler() {
const newValue = !ConfigUtil.getConfigItem("errorReporting", true);
ConfigUtil.setConfigItem("errorReporting", newValue);
enableErrorReporting();
},
});
}
async function customCssDialog(): Promise<void> {
const showDialogOptions: OpenDialogOptions = {
title: "Select file",
properties: ["openFile"],
filters: [{name: "CSS file", extensions: ["css"]}],
};
const {filePaths, canceled} = await dialog.showOpenDialog(
showDialogOptions,
);
if (!canceled) {
ConfigUtil.setConfigItem("customCSS", filePaths[0]);
ipcRenderer.send("forward-message", "hard-reload");
}
}
function setLocale(): void {
const langDiv: HTMLSelectElement = $root.querySelector(".lang-div")!;
const langListHtml = generateSelectHtml(supportedLocales, "lang-menu");
langDiv.innerHTML += langListHtml.html;
// `langMenu` is the select-option dropdown menu formed after executing the previous command
const langMenu: HTMLSelectElement = $root.querySelector(".lang-menu")!;
// The next three lines set the selected language visible on the dropdown button
let language = ConfigUtil.getConfigItem("appLanguage", "en");
language =
language && langMenu.options.namedItem(language) ? language : "en";
langMenu.options.namedItem(language)!.selected = true;
langMenu.addEventListener("change", () => {
ConfigUtil.setConfigItem("appLanguage", langMenu.value);
});
}
function minimizeOnStart(): void {
generateSettingOption({
$element: $root.querySelector("#start-minimize-option .setting-control")!,
value: ConfigUtil.getConfigItem("startMinimized", false),
clickHandler() {
const newValue = !ConfigUtil.getConfigItem("startMinimized", false);
ConfigUtil.setConfigItem("startMinimized", newValue);
minimizeOnStart();
},
});
}
function addCustomCss(): void {
const customCssButton = $root.querySelector(
"#add-custom-css .custom-css-button",
)!;
customCssButton.addEventListener("click", async () => {
await customCssDialog();
});
}
function showCustomCssPath(): void {
if (!ConfigUtil.getConfigItem("customCSS", null)) {
const cssPath: HTMLElement = $root.querySelector("#remove-custom-css")!;
cssPath.style.display = "none";
}
}
function removeCustomCss(): void {
const removeCssButton = $root.querySelector("#css-delete-action")!;
removeCssButton.addEventListener("click", () => {
ConfigUtil.setConfigItem("customCSS", "");
ipcRenderer.send("forward-message", "hard-reload");
});
}
async function downloadFolderDialog(): Promise<void> {
const showDialogOptions: OpenDialogOptions = {
title: "Select Download Location",
properties: ["openDirectory"],
};
const {filePaths, canceled} = await dialog.showOpenDialog(
showDialogOptions,
);
if (!canceled) {
ConfigUtil.setConfigItem("downloadsPath", filePaths[0]);
const downloadFolderPath: HTMLElement = $root.querySelector(
".download-folder-path",
)!;
downloadFolderPath.textContent = filePaths[0];
}
}
function downloadFolder(): void {
const downloadFolder = $root.querySelector(
"#download-folder .download-folder-button",
)!;
downloadFolder.addEventListener("click", async () => {
await downloadFolderDialog();
});
}
function updatePromptDownloadOption(): void {
generateSettingOption({
$element: $root.querySelector("#prompt-download .setting-control")!,
value: ConfigUtil.getConfigItem("promptDownload", false),
clickHandler() {
const newValue = !ConfigUtil.getConfigItem("promptDownload", false);
ConfigUtil.setConfigItem("promptDownload", newValue);
updatePromptDownloadOption();
},
});
}
async function factoryResetSettings(): Promise<void> {
const clearAppDataMessage =
"When the application restarts, it will be as if you have just downloaded Zulip app.";
const getAppPath = path.join(app.getPath("appData"), app.name);
const {response} = await dialog.showMessageBox({
type: "warning",
buttons: ["YES", "NO"],
defaultId: 0,
message: "Are you sure?",
detail: clearAppDataMessage,
});
if (response === 0) {
await fs.promises.rmdir(getAppPath, {recursive: true});
setTimeout(() => {
ipcRenderer.send("clear-app-settings");
}, 1000);
}
}
function factoryReset(): void {
const factoryResetButton = $root.querySelector(
"#factory-reset-option .factory-reset-button",
)!;
factoryResetButton.addEventListener("click", async () => {
await factoryResetSettings();
});
}
function initSpellChecker(): void {
// The Electron API is a no-op on macOS and macOS default spellchecker is used.
if (process.platform === "darwin") {
const note: HTMLElement = $root.querySelector("#note")!;
note.append(t.__("On macOS, the OS spellchecker is used."));
note.append(document.createElement("br"));
note.append(
t.__(
"Change the language from System Preferences → Keyboard → Text → Spelling.",
),
);
} else {
const note: HTMLElement = $root.querySelector("#note")!;
note.append(
t.__("You can select a maximum of 3 languages for spellchecking."),
);
const spellDiv: HTMLElement = $root.querySelector("#spellcheck-langs")!;
spellDiv.innerHTML += html`
<div class="setting-description">${t.__("Spellchecker Languages")}</div>
<div id="spellcheck-langs-value">
<input name="spellcheck" placeholder="Enter Languages" />
</div>
`.html;
const availableLanguages = session.fromPartition(
"persist:webviewsession",
).availableSpellCheckerLanguages;
let languagePairs = new Map<string, string>();
for (const l of availableLanguages) {
if (ISO6391.validate(l)) {
languagePairs.set(ISO6391.getName(l), l);
}
}
// Manually set names for languages not available in ISO6391
languagePairs.set("English (AU)", "en-AU");
languagePairs.set("English (CA)", "en-CA");
languagePairs.set("English (GB)", "en-GB");
languagePairs.set("English (US)", "en-US");
languagePairs.set("Spanish (Latin America)", "es-419");
languagePairs.set("Spanish (Argentina)", "es-AR");
languagePairs.set("Spanish (Mexico)", "es-MX");
languagePairs.set("Spanish (US)", "es-US");
languagePairs.set("Portuguese (Brazil)", "pt-BR");
languagePairs.set("Portuguese (Portugal)", "pt-PT");
languagePairs.set("Serbo-Croatian", "sh");
languagePairs = new Map(
[...languagePairs].sort((a, b) => (a[0] < b[0] ? -1 : 1)),
);
const tagField: HTMLInputElement = $root.querySelector(
"input[name=spellcheck]",
)!;
const tagify = new Tagify(tagField, {
whitelist: [...languagePairs.keys()],
enforceWhitelist: true,
maxTags: 3,
dropdown: {
enabled: 0,
maxItems: Number.POSITIVE_INFINITY,
closeOnSelect: false,
highlightFirst: true,
position: "manual",
classname: "settings-tagify-dropdown",
},
});
tagify.DOM.input.addEventListener("focus", () => {
tagify.dropdown.show();
$root
.querySelector("#spellcheck-langs-value")!
.append(tagify.DOM.dropdown);
});
tagify.DOM.input.addEventListener("blur", () => {
tagify.dropdown.hide(true);
tagify.DOM.dropdown.remove();
});
const configuredLanguages: string[] = (
ConfigUtil.getConfigItem("spellcheckerLanguages", null) ?? []
).map(
(code: string) =>
[...languagePairs].find((pair) => pair[1] === code)![0],
);
tagify.addTags(configuredLanguages);
tagField.addEventListener("change", () => {
if (tagField.value.length === 0) {
ConfigUtil.setConfigItem("spellcheckerLanguages", []);
ipcRenderer.send("configure-spell-checker");
} else {
const data: unknown = JSON.parse(tagField.value);
const spellLangs: string[] = z
.array(z.object({value: z.string()}))
.parse(data)
.map((elt) => languagePairs.get(elt.value)!);
ConfigUtil.setConfigItem("spellcheckerLanguages", spellLangs);
ipcRenderer.send("configure-spell-checker");
}
});
}
// Do not display the spellchecker input and note if it is disabled
if (!ConfigUtil.getConfigItem("enableSpellchecker", true)) {
const spellcheckerLanguageInput: HTMLElement =
$root.querySelector("#spellcheck-langs")!;
const spellcheckerNote: HTMLElement = $root.querySelector("#note")!;
spellcheckerLanguageInput.style.display = "none";
spellcheckerNote.style.display = "none";
}
}
}

View File

@@ -1,67 +0,0 @@
'use strict';
const BaseComponent = require(__dirname + '/../../components/base.js');
class PreferenceNav extends BaseComponent {
constructor(props) {
super();
this.props = props;
this.navItems = ['General', 'Servers'];
this.init();
}
template() {
let navItemsTemplate = '';
for (const navItem of this.navItems) {
navItemsTemplate += `<div class="nav" id="nav-${navItem}">${navItem}</div>`;
}
return `
<div>
<div id="settings-header">Settings</div>
<div id="nav-container">${navItemsTemplate}</div>
</div>
`;
}
init() {
this.$el = this.generateNodeFromTemplate(this.template());
this.props.$root.appendChild(this.$el);
this.registerListeners();
}
registerListeners() {
for (const navItem of this.navItems) {
const $item = document.getElementById(`nav-${navItem}`);
$item.addEventListener('click', () => {
this.props.onItemSelected(navItem);
});
}
}
select(navItemToSelect) {
for (const navItem of this.navItems) {
if (navItem === navItemToSelect) {
this.activate(navItem);
} else {
this.deactivate(navItem);
}
}
}
activate(navItem) {
const $item = document.getElementById(`nav-${navItem}`);
$item.classList.add('active');
}
deactivate(navItem) {
const $item = document.getElementById(`nav-${navItem}`);
$item.classList.remove('active');
}
}
module.exports = PreferenceNav;

View File

@@ -0,0 +1,74 @@
import type {Html} from "../../../../common/html.js";
import {html} from "../../../../common/html.js";
import * as t from "../../../../common/translation-util.js";
import type {NavItem} from "../../../../common/types.js";
import {generateNodeFromHtml} from "../../components/base.js";
type PreferenceNavProps = {
$root: Element;
onItemSelected: (navItem: NavItem) => void;
};
export default class PreferenceNav {
navItems: NavItem[];
$el: Element;
constructor(private readonly props: PreferenceNavProps) {
this.navItems = [
"General",
"Network",
"AddServer",
"Organizations",
"Shortcuts",
];
this.$el = generateNodeFromHtml(this.templateHtml());
this.props.$root.append(this.$el);
this.registerListeners();
}
templateHtml(): Html {
const navItemsHtml = html``.join(
this.navItems.map(
(navItem) => html`
<div class="nav" id="nav-${navItem}">${t.__(navItem)}</div>
`,
),
);
return html`
<div>
<div id="settings-header">${t.__("Settings")}</div>
<div id="nav-container">${navItemsHtml}</div>
</div>
`;
}
registerListeners(): void {
for (const navItem of this.navItems) {
const $item = this.$el.querySelector(`#nav-${CSS.escape(navItem)}`)!;
$item.addEventListener("click", () => {
this.props.onItemSelected(navItem);
});
}
}
select(navItemToSelect: NavItem): void {
for (const navItem of this.navItems) {
if (navItem === navItemToSelect) {
this.activate(navItem);
} else {
this.deactivate(navItem);
}
}
}
activate(navItem: NavItem): void {
const $item = this.$el.querySelector(`#nav-${CSS.escape(navItem)}`)!;
$item.classList.add("active");
}
deactivate(navItem: NavItem): void {
const $item = this.$el.querySelector(`#nav-${CSS.escape(navItem)}`)!;
$item.classList.remove("active");
}
}

View File

@@ -0,0 +1,136 @@
import * as ConfigUtil from "../../../../common/config-util.js";
import {html} from "../../../../common/html.js";
import * as t from "../../../../common/translation-util.js";
import {ipcRenderer} from "../../typed-ipc-renderer.js";
import {generateSettingOption} from "./base-section.js";
type NetworkSectionProps = {
$root: Element;
};
export function initNetworkSection({$root}: NetworkSectionProps): void {
$root.innerHTML = html`
<div class="settings-pane">
<div class="title">${t.__("Proxy")}</div>
<div id="appearance-option-settings" class="settings-card">
<div class="setting-row" id="use-system-settings">
<div class="setting-description">
${t.__("Use system proxy settings (requires restart)")}
</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="use-manual-settings">
<div class="setting-description">
${t.__("Manual proxy configuration")}
</div>
<div class="setting-control"></div>
</div>
<div class="manual-proxy-block">
<div class="setting-row" id="proxy-pac-option">
<span class="setting-input-key">PAC ${t.__("script")}</span>
<input
class="setting-input-value"
placeholder="e.g. foobar.com/pacfile.js"
/>
</div>
<div class="setting-row" id="proxy-rules-option">
<span class="setting-input-key">${t.__("Proxy rules")}</span>
<input
class="setting-input-value"
placeholder="e.g. http=foopy:80;ftp=foopy2"
/>
</div>
<div class="setting-row" id="proxy-bypass-option">
<span class="setting-input-key">${t.__("Proxy bypass rules")}</span>
<input class="setting-input-value" placeholder="e.g. foobar.com" />
</div>
<div class="setting-row">
<div class="action green" id="proxy-save-action">
<span>${t.__("Save")}</span>
</div>
</div>
</div>
</div>
</div>
`.html;
const $proxyPac: HTMLInputElement = $root.querySelector(
"#proxy-pac-option .setting-input-value",
)!;
const $proxyRules: HTMLInputElement = $root.querySelector(
"#proxy-rules-option .setting-input-value",
)!;
const $proxyBypass: HTMLInputElement = $root.querySelector(
"#proxy-bypass-option .setting-input-value",
)!;
const $proxySaveAction = $root.querySelector("#proxy-save-action")!;
const $manualProxyBlock = $root.querySelector(".manual-proxy-block")!;
toggleManualProxySettings(ConfigUtil.getConfigItem("useManualProxy", false));
updateProxyOption();
$proxyPac.value = ConfigUtil.getConfigItem("proxyPAC", "");
$proxyRules.value = ConfigUtil.getConfigItem("proxyRules", "");
$proxyBypass.value = ConfigUtil.getConfigItem("proxyBypass", "");
$proxySaveAction.addEventListener("click", () => {
ConfigUtil.setConfigItem("proxyPAC", $proxyPac.value);
ConfigUtil.setConfigItem("proxyRules", $proxyRules.value);
ConfigUtil.setConfigItem("proxyBypass", $proxyBypass.value);
ipcRenderer.send("forward-message", "reload-proxy", true);
});
function toggleManualProxySettings(option: boolean): void {
$manualProxyBlock.classList.toggle("hidden", !option);
}
function updateProxyOption(): void {
generateSettingOption({
$element: $root.querySelector("#use-system-settings .setting-control")!,
value: ConfigUtil.getConfigItem("useSystemProxy", false),
clickHandler() {
const newValue = !ConfigUtil.getConfigItem("useSystemProxy", false);
const manualProxyValue = ConfigUtil.getConfigItem(
"useManualProxy",
false,
);
if (manualProxyValue && newValue) {
ConfigUtil.setConfigItem("useManualProxy", !manualProxyValue);
toggleManualProxySettings(!manualProxyValue);
}
if (!newValue) {
// Remove proxy system proxy settings
ConfigUtil.setConfigItem("proxyRules", "");
ipcRenderer.send("forward-message", "reload-proxy", false);
}
ConfigUtil.setConfigItem("useSystemProxy", newValue);
updateProxyOption();
},
});
generateSettingOption({
$element: $root.querySelector("#use-manual-settings .setting-control")!,
value: ConfigUtil.getConfigItem("useManualProxy", false),
clickHandler() {
const newValue = !ConfigUtil.getConfigItem("useManualProxy", false);
const systemProxyValue = ConfigUtil.getConfigItem(
"useSystemProxy",
false,
);
toggleManualProxySettings(newValue);
if (systemProxyValue && newValue) {
ConfigUtil.setConfigItem("useSystemProxy", !systemProxyValue);
}
ConfigUtil.setConfigItem("proxyRules", "");
ConfigUtil.setConfigItem("useManualProxy", newValue);
// Reload app only when turning manual proxy off, hence !newValue
ipcRenderer.send("forward-message", "reload-proxy", !newValue);
updateProxyOption();
},
});
}
}

View File

@@ -1,57 +0,0 @@
'use strict';
const BaseComponent = require(__dirname + '/../../components/base.js');
const DomainUtil = require(__dirname + '/../../utils/domain-util.js');
class NewServerForm extends BaseComponent {
constructor(props) {
super();
this.props = props;
}
template() {
return `
<div class="settings-card" style="border: solid 1px #4CAF50;">
<div class="server-info-right">
<div class="server-info-row">
<input class="server-info-url" autofocus placeholder="Enter the url of your Zulip server..."/>
</div>
<div class="server-info-row">
<div class="action green server-save-action">
<i class="material-icons">check_box</i>
<span>Save</span>
</div>
</div>
</div>
</div>
`;
}
init() {
this.initForm();
this.initActions();
}
initForm() {
this.$newServerForm = this.generateNodeFromTemplate(this.template());
this.$saveServerButton = this.$newServerForm.getElementsByClassName('server-save-action')[0];
this.props.$root.innerHTML = '';
this.props.$root.appendChild(this.$newServerForm);
this.$newServerUrl = this.$newServerForm.querySelectorAll('input.server-info-url')[0];
}
initActions() {
this.$saveServerButton.addEventListener('click', () => {
DomainUtil.checkDomain(this.$newServerUrl.value).then(serverConf => {
DomainUtil.addDomain(serverConf).then(() => {
this.props.onChange(this.props.index);
});
}, errorMessage => {
alert(errorMessage);
});
});
}
}
module.exports = NewServerForm;

View File

@@ -0,0 +1,103 @@
import {dialog} from "@electron/remote";
import {html} from "../../../../common/html.js";
import * as LinkUtil from "../../../../common/link-util.js";
import * as t from "../../../../common/translation-util.js";
import {generateNodeFromHtml} from "../../components/base.js";
import {ipcRenderer} from "../../typed-ipc-renderer.js";
import * as DomainUtil from "../../utils/domain-util.js";
type NewServerFormProps = {
$root: Element;
onChange: () => void;
};
export function initNewServerForm({$root, onChange}: NewServerFormProps): void {
const $newServerForm = generateNodeFromHtml(html`
<div class="server-input-container">
<div class="title">${t.__("Organization URL")}</div>
<div class="add-server-info-row">
<input
class="setting-input-value"
autofocus
placeholder="your-organization.zulipchat.com or zulip.your-organization.com"
/>
</div>
<div class="server-center">
<button id="connect">${t.__("Connect")}</button>
</div>
<div class="server-center">
<div class="divider">
<hr class="left" />
${t.__("OR")}
<hr class="right" />
</div>
</div>
<div class="server-center">
<button id="open-create-org-link">
${t.__("Create a new organization")}
</button>
</div>
<div class="server-center">
<div class="server-network-option">
<span id="open-network-settings"
>${t.__("Network and Proxy Settings")}</span
>
<i class="material-icons open-network-button">open_in_new</i>
</div>
</div>
</div>
`);
const $saveServerButton: HTMLButtonElement =
$newServerForm.querySelector("#connect")!;
$root.textContent = "";
$root.append($newServerForm);
const $newServerUrl: HTMLInputElement = $newServerForm.querySelector(
"input.setting-input-value",
)!;
async function submitFormHandler(): Promise<void> {
$saveServerButton.textContent = "Connecting...";
let serverConf;
try {
serverConf = await DomainUtil.checkDomain($newServerUrl.value.trim());
} catch (error: unknown) {
$saveServerButton.textContent = "Connect";
await dialog.showMessageBox({
type: "error",
message:
error instanceof Error
? `${error.name}: ${error.message}`
: "Unknown error",
buttons: ["OK"],
});
return;
}
await DomainUtil.addDomain(serverConf);
onChange();
}
$saveServerButton.addEventListener("click", async () => {
await submitFormHandler();
});
$newServerUrl.addEventListener("keypress", async (event) => {
if (event.key === "Enter") {
await submitFormHandler();
}
});
// Open create new org link in default browser
const link = "https://zulip.com/new/";
const externalCreateNewOrgElement = $root.querySelector(
"#open-create-org-link",
)!;
externalCreateNewOrgElement.addEventListener("click", async () => {
await LinkUtil.openBrowser(new URL(link));
});
const networkSettingsId = $root.querySelector(".server-network-option")!;
networkSettingsId.addEventListener("click", () => {
ipcRenderer.send("forward-message", "open-network-settings");
});
}

View File

@@ -1,68 +0,0 @@
'use strict';
const BaseComponent = require(__dirname + '/js/components/base.js');
const {ipcRenderer} = require('electron');
const Nav = require(__dirname + '/js/pages/preference/nav.js');
const ServersSection = require(__dirname + '/js/pages/preference/servers-section.js');
const GeneralSection = require(__dirname + '/js/pages/preference/general-section.js');
class PreferenceView extends BaseComponent {
constructor() {
super();
this.$sidebarContainer = document.getElementById('sidebar');
this.$settingsContainer = document.getElementById('settings-container');
}
init() {
this.nav = new Nav({
$root: this.$sidebarContainer,
onItemSelected: this.handleNavigation.bind(this)
});
this.setDefaultView();
this.registerIpcs();
}
setDefaultView() {
let nav = 'General';
const hasTag = window.location.hash;
if (hasTag) {
nav = hasTag.substring(1);
}
this.handleNavigation(nav);
}
handleNavigation(navItem) {
this.nav.select(navItem);
switch (navItem) {
case 'Servers': {
this.section = new ServersSection({
$root: this.$settingsContainer
});
break;
}
case 'General': {
this.section = new GeneralSection({
$root: this.$settingsContainer
});
break;
}
default: break;
}
this.section.init();
window.location.hash = `#${navItem}`;
}
registerIpcs() {
ipcRenderer.on('switch-settings-nav', (event, navItem) => {
this.handleNavigation(navItem);
});
}
}
window.onload = () => {
const preferenceView = new PreferenceView();
preferenceView.init();
};

View File

@@ -0,0 +1,141 @@
import process from "node:process";
import type {DndSettings} from "../../../../common/dnd-util.js";
import {bundleUrl} from "../../../../common/paths.js";
import type {NavItem} from "../../../../common/types.js";
import {ipcRenderer} from "../../typed-ipc-renderer.js";
import {initConnectedOrgSection} from "./connected-org-section.js";
import {initGeneralSection} from "./general-section.js";
import Nav from "./nav.js";
import {initNetworkSection} from "./network-section.js";
import {initServersSection} from "./servers-section.js";
import {initShortcutsSection} from "./shortcuts-section.js";
export class PreferenceView {
static async create(): Promise<PreferenceView> {
return new PreferenceView(
await (
await fetch(new URL("app/renderer/preference.html", bundleUrl))
).text(),
);
}
readonly $view: HTMLElement;
private readonly $shadow: ShadowRoot;
private readonly $settingsContainer: Element;
private readonly nav: Nav;
private navItem: NavItem = "General";
private constructor(templateHtml: string) {
this.$view = document.createElement("div");
this.$shadow = this.$view.attachShadow({mode: "open"});
this.$shadow.innerHTML = templateHtml;
const $sidebarContainer = this.$shadow.querySelector("#sidebar")!;
this.$settingsContainer = this.$shadow.querySelector(
"#settings-container",
)!;
this.nav = new Nav({
$root: $sidebarContainer,
onItemSelected: this.handleNavigation,
});
ipcRenderer.on("toggle-sidebar", this.handleToggleSidebar);
ipcRenderer.on("toggle-autohide-menubar", this.handleToggleMenubar);
ipcRenderer.on("toggle-dnd", this.handleToggleDnd);
this.handleNavigation(this.navItem);
}
handleNavigation = (navItem: NavItem): void => {
this.navItem = navItem;
this.nav.select(navItem);
switch (navItem) {
case "AddServer": {
initServersSection({
$root: this.$settingsContainer,
});
break;
}
case "General": {
initGeneralSection({
$root: this.$settingsContainer,
});
break;
}
case "Organizations": {
initConnectedOrgSection({
$root: this.$settingsContainer,
});
break;
}
case "Network": {
initNetworkSection({
$root: this.$settingsContainer,
});
break;
}
case "Shortcuts": {
initShortcutsSection({
$root: this.$settingsContainer,
});
break;
}
default: {
((n: never) => n)(navItem);
}
}
window.location.hash = `#${navItem}`;
};
handleToggleTray(state: boolean) {
this.handleToggle("tray-option", state);
}
destroy(): void {
ipcRenderer.off("toggle-sidebar", this.handleToggleSidebar);
ipcRenderer.off("toggle-autohide-menubar", this.handleToggleMenubar);
ipcRenderer.off("toggle-dnd", this.handleToggleDnd);
}
// Handle toggling and reflect changes in preference page
private handleToggle(elementName: string, state = false): void {
const inputSelector = `#${elementName} .action .switch input`;
const input: HTMLInputElement = this.$shadow.querySelector(inputSelector)!;
if (input) {
input.checked = state;
}
}
private readonly handleToggleSidebar = (_event: Event, state: boolean) => {
this.handleToggle("sidebar-option", state);
};
private readonly handleToggleMenubar = (_event: Event, state: boolean) => {
this.handleToggle("menubar-option", state);
};
private readonly handleToggleDnd = (
_event: Event,
_state: boolean,
newSettings: Partial<DndSettings>,
) => {
this.handleToggle("show-notification-option", newSettings.showNotification);
this.handleToggle("silent-option", newSettings.silent);
if (process.platform === "win32") {
this.handleToggle(
"flash-taskbar-option",
newSettings.flashTaskbarOnMessage,
);
}
};
}

View File

@@ -1,77 +0,0 @@
'use strict';
const {dialog} = require('electron').remote;
const {ipcRenderer} = require('electron');
const BaseComponent = require(__dirname + '/../../components/base.js');
const DomainUtil = require(__dirname + '/../../utils/domain-util.js');
class ServerInfoForm extends BaseComponent {
constructor(props) {
super();
this.props = props;
}
template() {
return `
<div class="settings-card">
<div class="server-info-left">
<img class="server-info-icon" src="${this.props.server.icon}"/>
</div>
<div class="server-info-right">
<div class="server-info-row">
<span class="server-info-alias">${this.props.server.alias}</span>
<i class="material-icons open-tab-button">open_in_new</i>
</div>
<div class="server-info-row">
<input class="server-info-url" disabled value="${this.props.server.url}"/>
</div>
<div class="server-info-row">
<div class="action red server-delete-action">
<i class="material-icons">indeterminate_check_box</i>
<span>Delete</span>
</div>
</div>
</div>
</div>
`;
}
init() {
this.initForm();
this.initActions();
}
initForm() {
this.$serverInfoForm = this.generateNodeFromTemplate(this.template());
this.$serverInfoAlias = this.$serverInfoForm.getElementsByClassName('server-info-alias')[0];
this.$deleteServerButton = this.$serverInfoForm.getElementsByClassName('server-delete-action')[0];
this.$openServerButton = this.$serverInfoForm.getElementsByClassName('open-tab-button')[0];
this.props.$root.appendChild(this.$serverInfoForm);
}
initActions() {
this.$deleteServerButton.addEventListener('click', () => {
dialog.showMessageBox({
type: 'warning',
buttons: ['YES', 'NO'],
defaultId: 0,
message: 'Are you sure you want to delete this server?'
}, response => {
if (response === 0) {
DomainUtil.removeDomain(this.props.index);
this.props.onChange(this.props.index);
}
});
});
this.$openServerButton.addEventListener('click', () => {
ipcRenderer.send('forward-message', 'switch-server-tab', this.props.index);
});
this.$serverInfoAlias.addEventListener('click', () => {
ipcRenderer.send('forward-message', 'switch-server-tab', this.props.index);
});
}
}
module.exports = ServerInfoForm;

View File

@@ -0,0 +1,83 @@
import {dialog} from "@electron/remote";
import {html} from "../../../../common/html.js";
import * as Messages from "../../../../common/messages.js";
import * as t from "../../../../common/translation-util.js";
import type {ServerConf} from "../../../../common/types.js";
import {generateNodeFromHtml} from "../../components/base.js";
import {ipcRenderer} from "../../typed-ipc-renderer.js";
import * as DomainUtil from "../../utils/domain-util.js";
type ServerInfoFormProps = {
$root: Element;
server: ServerConf;
index: number;
onChange: () => void;
};
export function initServerInfoForm(props: ServerInfoFormProps): void {
const $serverInfoForm = generateNodeFromHtml(html`
<div class="settings-card">
<div class="server-info-left">
<img
class="server-info-icon"
src="${DomainUtil.iconAsUrl(props.server.icon)}"
/>
<div class="server-info-row">
<span class="server-info-alias">${props.server.alias}</span>
<i class="material-icons open-tab-button">open_in_new</i>
</div>
</div>
<div class="server-info-right">
<div class="server-info-row server-url">
<span class="server-url-info" title="${props.server.url}"
>${props.server.url}</span
>
</div>
<div class="server-info-row">
<div class="action red server-delete-action">
<span>${t.__("Disconnect")}</span>
</div>
</div>
</div>
</div>
`);
const $serverInfoAlias = $serverInfoForm.querySelector(".server-info-alias")!;
const $serverIcon = $serverInfoForm.querySelector(".server-info-icon")!;
const $deleteServerButton = $serverInfoForm.querySelector(
".server-delete-action",
)!;
const $openServerButton = $serverInfoForm.querySelector(".open-tab-button")!;
props.$root.append($serverInfoForm);
$deleteServerButton.addEventListener("click", async () => {
const {response} = await dialog.showMessageBox({
type: "warning",
buttons: [t.__("YES"), t.__("NO")],
defaultId: 0,
message: t.__("Are you sure you want to disconnect this organization?"),
});
if (response === 0) {
if (DomainUtil.removeDomain(props.index)) {
ipcRenderer.send("reload-full-app");
} else {
const {title, content} = Messages.orgRemovalError(
DomainUtil.getDomain(props.index).url,
);
dialog.showErrorBox(title, content);
}
}
});
$openServerButton.addEventListener("click", () => {
ipcRenderer.send("forward-message", "switch-server-tab", props.index);
});
$serverInfoAlias.addEventListener("click", () => {
ipcRenderer.send("forward-message", "switch-server-tab", props.index);
});
$serverIcon.addEventListener("click", () => {
ipcRenderer.send("forward-message", "switch-server-tab", props.index);
});
}

View File

@@ -1,81 +0,0 @@
'use strict';
const {ipcRenderer} = require('electron');
const BaseComponent = require(__dirname + '/../../components/base.js');
const DomainUtil = require(__dirname + '/../../utils/domain-util.js');
const ServerInfoForm = require(__dirname + '/server-info-form.js');
const NewServerForm = require(__dirname + '/new-server-form.js');
class ServersSection extends BaseComponent {
constructor(props) {
super();
this.props = props;
}
template() {
return `
<div class="settings-pane" id="server-settings-pane">
<div class="title">Manage Servers</div>
<div class="actions-container">
<div class="action green" id="new-server-action">
<i class="material-icons">add_box</i>
<span>Add Server</span>
</div>
</div>
<div id="new-server-container" class="hidden"></div>
<div class="sub-title" id="existing-servers"></div>
<div id="server-info-container"></div>
</div>
`;
}
init() {
this.initServers();
this.initActions();
}
initServers() {
this.props.$root.innerHTML = '';
const servers = DomainUtil.getDomains();
this.props.$root.innerHTML = this.template();
this.$serverInfoContainer = document.getElementById('server-info-container');
this.$existingServers = document.getElementById('existing-servers');
this.$newServerContainer = document.getElementById('new-server-container');
this.$newServerButton = document.getElementById('new-server-action');
this.$serverInfoContainer.innerHTML = servers.length ? '' : 'Add your first server to get started!';
// Show Existing servers if servers are there otherwise hide it
this.$existingServers.innerHTML = servers.length === 0 ? '' : 'Existing servers';
this.initNewServerForm();
for (let i = 0; i < servers.length; i++) {
new ServerInfoForm({
$root: this.$serverInfoContainer,
server: servers[i],
index: i,
onChange: this.handleServerInfoChange.bind(this)
}).init();
}
}
initNewServerForm() {
new NewServerForm({
$root: this.$newServerContainer,
onChange: this.handleServerInfoChange.bind(this)
}).init();
}
initActions() {
this.$newServerContainer.classList.remove('hidden');
this.$newServerButton.classList.remove('green');
this.$newServerButton.classList.add('grey');
}
handleServerInfoChange() {
ipcRenderer.send('forward-message', 'reload-viewer');
}
}
module.exports = ServersSection;

View File

@@ -0,0 +1,28 @@
import {html} from "../../../../common/html.js";
import * as t from "../../../../common/translation-util.js";
import {reloadApp} from "./base-section.js";
import {initNewServerForm} from "./new-server-form.js";
type ServersSectionProps = {
$root: Element;
};
export function initServersSection({$root}: ServersSectionProps): void {
$root.innerHTML = html`
<div class="add-server-modal">
<div class="modal-container">
<div class="settings-pane" id="server-settings-pane">
<div class="page-title">${t.__("Add a Zulip organization")}</div>
<div id="new-server-container"></div>
</div>
</div>
</div>
`.html;
const $newServerContainer = $root.querySelector("#new-server-container")!;
initNewServerForm({
$root: $newServerContainer,
onChange: reloadApp,
});
}

View File

@@ -0,0 +1,233 @@
import process from "node:process";
import {html} from "../../../../common/html.js";
import * as LinkUtil from "../../../../common/link-util.js";
import * as t from "../../../../common/translation-util.js";
type ShortcutsSectionProps = {
$root: Element;
};
// eslint-disable-next-line complexity
export function initShortcutsSection({$root}: ShortcutsSectionProps): void {
const cmdOrCtrl = process.platform === "darwin" ? "⌘" : "Ctrl";
$root.innerHTML = html`
<div class="settings-pane">
<div class="settings-card tip">
<p>
<b><i class="material-icons md-14">settings</i>${t.__("Tip")}: </b
>${t.__("These desktop app shortcuts extend the Zulip webapp's")}
<span id="open-hotkeys-link"> ${t.__("keyboard shortcuts")}</span>.
</p>
</div>
<div class="title">${t.__("Application Shortcuts")}</div>
<div class="settings-card">
<table>
<tr>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>,</kbd></td>
<td>${t.__("Settings")}</td>
</tr>
<tr>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>K</kbd></td>
<td>${t.__("Keyboard Shortcuts")}</td>
</tr>
<tr ${process.platform === "darwin" ? "hidden" : ""}>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>Shift</kbd> + <kbd>M</kbd></td>
<td>${t.__("Toggle Do Not Disturb")}</td>
</tr>
<tr ${process.platform === "darwin" ? "" : "hidden"}>
<td><kbd>Shift</kbd> + <kbd>${cmdOrCtrl}</kbd> + <kbd>M</kbd></td>
<td>${t.__("Toggle Do Not Disturb")}</td>
</tr>
<tr ${process.platform === "darwin" ? "" : "hidden"}>
<td><kbd>Shift</kbd> + <kbd>${cmdOrCtrl}</kbd> + <kbd>D</kbd></td>
<td>${t.__("Reset App Settings")}</td>
</tr>
<tr>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>L</kbd></td>
<td>${t.__("Log Out")}</td>
</tr>
<tr ${process.platform === "darwin" ? "" : "hidden"}>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>H</kbd></td>
<td>${t.__("Hide Zulip")}</td>
</tr>
<tr ${process.platform === "darwin" ? "" : "hidden"}>
<td><kbd>Option</kbd> + <kbd>${cmdOrCtrl}</kbd> + <kbd>H</kbd></td>
<td>${t.__("Hide Others")}</td>
</tr>
<tr>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>Q</kbd></td>
<td>${t.__("Quit Zulip")}</td>
</tr>
</table>
<div class="setting-control"></div>
</div>
<div class="title">${t.__("Edit Shortcuts")}</div>
<div class="settings-card">
<table>
<tr>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>Z</kbd></td>
<td>${t.__("Undo")}</td>
</tr>
<tr ${process.platform === "darwin" ? "" : "hidden"}>
<td><kbd>Shift</kbd> + <kbd>${cmdOrCtrl}</kbd> + <kbd>Z</kbd></td>
<td>${t.__("Redo")}</td>
</tr>
<tr ${process.platform === "darwin" ? "hidden" : ""}>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>Y</kbd></td>
<td>${t.__("Redo")}</td>
</tr>
<tr>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>X</kbd></td>
<td>${t.__("Cut")}</td>
</tr>
<tr>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>C</kbd></td>
<td>${t.__("Copy")}</td>
</tr>
<tr>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>V</kbd></td>
<td>${t.__("Paste")}</td>
</tr>
<tr ${process.platform === "darwin" ? "hidden" : ""}>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>Shift</kbd> + <kbd>V</kbd></td>
<td>${t.__("Paste and Match Style")}</td>
</tr>
<tr ${process.platform === "darwin" ? "" : "hidden"}>
<td><kbd>Shift</kbd> + <kbd>${cmdOrCtrl}</kbd> + <kbd>V</kbd></td>
<td>${t.__("Paste and Match Style")}</td>
</tr>
<tr>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>A</kbd></td>
<td>${t.__("Select All")}</td>
</tr>
<tr ${process.platform === "darwin" ? "" : "hidden"}>
<td>
<kbd>Control</kbd> + <kbd>${cmdOrCtrl}</kbd> + <kbd>Space</kbd>
</td>
<td>${t.__("Emoji & Symbols")}</td>
</tr>
</table>
<div class="setting-control"></div>
</div>
<div class="title">${t.__("View Shortcuts")}</div>
<div class="settings-card">
<table>
<tr>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>R</kbd></td>
<td>${t.__("Reload")}</td>
</tr>
<tr ${process.platform === "darwin" ? "hidden" : ""}>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>Shift</kbd> + <kbd>R</kbd></td>
<td>${t.__("Hard Reload")}</td>
</tr>
<tr ${process.platform === "darwin" ? "" : "hidden"}>
<td><kbd>Shift</kbd> + <kbd>${cmdOrCtrl}</kbd> + <kbd>R</kbd></td>
<td>${t.__("Hard Reload")}</td>
</tr>
<tr ${process.platform === "darwin" ? "hidden" : ""}>
<td><kbd>F11</kbd></td>
<td>${t.__("Toggle Full Screen")}</td>
</tr>
<tr ${process.platform === "darwin" ? "" : "hidden"}>
<td><kbd>Control</kbd> + <kbd>${cmdOrCtrl}</kbd> + <kbd>F</kbd></td>
<td>${t.__("Enter Full Screen")}</td>
</tr>
<tr ${process.platform === "darwin" ? "" : "hidden"}>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>+</kbd></td>
<td>${t.__("Zoom In")}</td>
</tr>
<tr ${process.platform === "darwin" ? "hidden" : ""}>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>=</kbd></td>
<td>${t.__("Zoom In")}</td>
</tr>
<tr>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>-</kbd></td>
<td>${t.__("Zoom Out")}</td>
</tr>
<tr>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>0</kbd></td>
<td>${t.__("Actual Size")}</td>
</tr>
<tr ${process.platform === "darwin" ? "hidden" : ""}>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>Shift</kbd> + <kbd>S</kbd></td>
<td>${t.__("Toggle Sidebar")}</td>
</tr>
<tr ${process.platform === "darwin" ? "" : "hidden"}>
<td><kbd>Shift</kbd> + <kbd>${cmdOrCtrl}</kbd> + <kbd>S</kbd></td>
<td>${t.__("Toggle Sidebar")}</td>
</tr>
<tr ${process.platform === "darwin" ? "" : "hidden"}>
<td><kbd>Option</kbd> + <kbd>${cmdOrCtrl}</kbd> + <kbd>I</kbd></td>
<td>${t.__("Toggle DevTools for Zulip App")}</td>
</tr>
<tr ${process.platform === "darwin" ? "hidden" : ""}>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>Shift</kbd> + <kbd>I</kbd></td>
<td>${t.__("Toggle DevTools for Zulip App")}</td>
</tr>
<tr ${process.platform === "darwin" ? "" : "hidden"}>
<td><kbd>Option</kbd> + <kbd>${cmdOrCtrl}</kbd> + <kbd>U</kbd></td>
<td>${t.__("Toggle DevTools for Active Tab")}</td>
</tr>
<tr ${process.platform === "darwin" ? "hidden" : ""}>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>Shift</kbd> + <kbd>U</kbd></td>
<td>${t.__("Toggle DevTools for Active Tab")}</td>
</tr>
<tr>
<td><kbd>Ctrl</kbd> + <kbd>Tab</kbd></td>
<td>${t.__("Switch to Next Organization")}</td>
</tr>
<tr>
<td><kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>Tab</kbd></td>
<td>${t.__("Switch to Previous Organization")}</td>
</tr>
</table>
<div class="setting-control"></div>
</div>
<div class="title">${t.__("History Shortcuts")}</div>
<div class="settings-card">
<table>
<tr ${process.platform === "darwin" ? "" : "hidden"}>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>←</kbd></td>
<td>${t.__("Back")}</td>
</tr>
<tr ${process.platform === "darwin" ? "hidden" : ""}>
<td><kbd>Alt</kbd> + <kbd>←</kbd></td>
<td>${t.__("Back")}</td>
</tr>
<tr ${process.platform === "darwin" ? "" : "hidden"}>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>→</kbd></td>
<td>${t.__("Forward")}</td>
</tr>
<tr ${process.platform === "darwin" ? "hidden" : ""}>
<td><kbd>Alt</kbd> + <kbd>→</kbd></td>
<td>${t.__("Forward")}</td>
</tr>
</table>
<div class="setting-control"></div>
</div>
<div class="title">${t.__("Window Shortcuts")}</div>
<div class="settings-card">
<table>
<tr>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>M</kbd></td>
<td>${t.__("Minimize")}</td>
</tr>
<tr>
<td><kbd>${cmdOrCtrl}</kbd> + <kbd>W</kbd></td>
<td>${t.__("Close")}</td>
</tr>
</table>
<div class="setting-control"></div>
</div>
</div>
`.html;
const link = "https://zulip.com/help/keyboard-shortcuts";
const externalCreateNewOrgElement =
$root.querySelector("#open-hotkeys-link")!;
externalCreateNewOrgElement.addEventListener("click", async () => {
await LinkUtil.openBrowser(new URL(link));
});
}

View File

@@ -1,42 +0,0 @@
'use strict';
const {ipcRenderer} = require('electron');
const {spellChecker} = require('./spellchecker');
// eslint-disable-next-line import/no-unassigned-import
require('./notification');
const logout = () => {
// Create the menu for the below
document.querySelector('.dropdown-toggle').click();
const nodes = document.querySelectorAll('.dropdown-menu li:last-child a');
nodes[nodes.length - 1].click();
};
const shortcut = () => {
// Create the menu for the below
const node = document.querySelector('a[data-overlay-trigger=keyboard-shortcuts]');
// Additional check
if (node.text.trim().toLowerCase() === 'keyboard shortcuts') {
node.click();
} else {
// Atleast click the dropdown
document.querySelector('.dropdown-toggle').click();
}
};
process.once('loaded', () => {
global.logout = logout;
global.shortcut = shortcut;
});
// To prevent failing this script on linux we need to load it after the document loaded
document.addEventListener('DOMContentLoaded', () => {
// Init spellchecker
spellChecker();
// redirect users to network troubleshooting page
document.querySelector('.restart_get_events_button').addEventListener('click', () => {
ipcRenderer.send('forward-message', 'reload-viewer');
});
});

View File

@@ -0,0 +1,29 @@
import {contextBridge} from "electron/renderer";
import electron_bridge, {bridgeEvents} from "./electron-bridge.js";
import * as NetworkError from "./pages/network.js";
import {ipcRenderer} from "./typed-ipc-renderer.js";
contextBridge.exposeInMainWorld("electron_bridge", electron_bridge);
ipcRenderer.on("logout", () => {
bridgeEvents.emit("logout");
});
ipcRenderer.on("show-keyboard-shortcuts", () => {
bridgeEvents.emit("show-keyboard-shortcuts");
});
ipcRenderer.on("show-notification-settings", () => {
bridgeEvents.emit("show-notification-settings");
});
window.addEventListener("load", () => {
if (!location.href.includes("app/renderer/network.html")) {
return;
}
const $reconnectButton = document.querySelector("#reconnect")!;
const $settingsButton = document.querySelector("#settings")!;
NetworkError.init($reconnectButton, $settingsButton);
});

View File

@@ -1,29 +0,0 @@
'use strict';
const {SpellCheckHandler, ContextMenuListener, ContextMenuBuilder} = require('electron-spellchecker');
function spellChecker() {
// Implement spellcheck using electron api
window.spellCheckHandler = new SpellCheckHandler();
window.spellCheckHandler.attachToInput();
// Start off as US English
window.spellCheckHandler.switchLanguage('en-US');
const contextMenuBuilder = new ContextMenuBuilder(window.spellCheckHandler);
const contextMenuListener = new ContextMenuListener(info => {
contextMenuBuilder.showPopupMenu(info);
});
// Clean up events after you navigate away from this page;
// otherwise, you may experience errors
window.addEventListener('beforeunload', () => {
// eslint-disable-next-line no-undef
spellCheckHandler.unsubscribe();
contextMenuListener.unsubscribe();
});
}
module.exports = {
spellChecker
};

Some files were not shown because too many files have changed in this diff Show More