Compare commits

...

1001 Commits

Author SHA1 Message Date
Akash Nimare
c4ce25f66c menu: Document enableMenu flag. 2018-10-28 15:10:34 +05:30
Akash Nimare
c6e8d27722 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.

Fixes: #587.
2018-10-27 19:25:28 +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
akashnimare
58f97038b1 🎉 v1.3.0-beta 2017-08-09 23:30:04 +05:30
akashnimare
6030d4c62a Updated build configuration 2017-08-09 23:27:24 +05:30
Akash Nimare
206f726e4a Merge pull request #268 from zulip/dev
Updated dev and app dependencies
2017-08-09 17:43:15 +00:00
akashnimare
9a36fffaac Updated app dependencies 2017-08-09 02:17:32 +05:30
akashnimare
30af4277e0 Updated devDependencies 2017-08-09 02:10:53 +05:30
akashnimare
98d23aaae1 document focus event 2017-08-08 23:51:04 +05:30
Akash Nimare
20feb9bd38 Merge pull request #266 from zulip/window-focus-fix
🎉 Focus webview contents on Window focus #216 #251
2017-08-08 18:07:17 +00:00
Akash Nimare
56090151c2 Merge pull request #267 from geeeeeeeeek/window-focus-fix
Enhance error handling.
2017-08-08 18:06:11 +00:00
Zhongyi Tong
84a69ce455 Enhance error handling. 2017-08-09 01:56:00 +08:00
akashnimare
2424f7a995 Focus webview contents on Window focus #216 #251 2017-08-08 22:13:03 +05:30
Akash Nimare
c3c60c98d6 Focus Webview elements on Linux/Windows #251 2017-08-07 15:51:56 +05:30
Akash Nimare
24017631c0 Merge pull request #265 from geeeeeeeeek/feature/switch-servers-in-menu
Feature/switch servers in menu
2017-08-06 12:29:29 +00:00
Akash Nimare
82a9d13a3c Add back History menu 2017-08-06 17:37:34 +05:30
Akash Nimare
c064322234 Added History submenu on Linux + Window 2017-08-06 17:30:33 +05:30
Zhongyi Tong
32542d500a Add tabs in window submenu. 2017-08-06 11:52:09 +08:00
akashnimare
5e8a971789 Do not show icon in new server form [WIP] #264 2017-08-06 01:01:48 +05:30
Zhongyi Tong
a7a9e96a58 Set application menu in AppMenu.setMenu instead of in main.js. 2017-08-06 00:25:56 +08:00
Zhongyi Tong
347f6e50eb Update require. 2017-08-06 00:21:49 +08:00
Zhongyi Tong
b873b358fe Refactor Menu.js. 2017-08-06 00:19:35 +08:00
akashnimare
caf4545902 focus new server input on page load [WIP] #264 2017-08-05 03:40:46 +05:30
Akash Nimare
9c2b7eeb27 Merge pull request #262 from geeeeeeeeek/issue/view-server-in-settings
Click server name in settings to show webview.
2017-08-04 08:44:05 +00:00
Zhongyi Tong
d25d71cb91 Click server name in settings to show webview. 2017-08-04 13:04:43 +08:00
Akash Nimare
289417e5a9 Added few common build errors 2017-08-03 03:34:18 +05:30
Akash Nimare
fd549e44a6 Merge pull request #261 from brockwhittaker/capitalize-menu-items
darwin-menu: Capitalize menu items to style match.
2017-08-02 21:54:44 +00:00
brockwhittaker
ae1ffe7ccc darwin-menu: Capitalize menu items to style match.
This capitalizes the menu items to be consistent with how all other
menu items are capitalized on almost all applications across Mac native
menus.
2017-08-02 14:46:38 -07:00
akashnimare
a04e14545e Remove unused dependency https 2017-08-03 02:46:28 +05:30
Akash Nimare
9b928a16b7 Merge pull request #259 from zulip/restore-window-position
restore window position [WIP] #231
2017-08-02 20:44:22 +00:00
akashnimare
b54cedbdbb restore window position [WIP] #231 2017-08-03 02:12:45 +05:30
Akash Nimare
3f9ba0a2bb Merge pull request #252 from gnprice/setup
Clarify and expand the developer docs a bit
2017-08-02 19:36:33 +00:00
Akash Nimare
9e8ec3b6d4 Merge pull request #256 from geeeeeeeeek/issue/delay-webview-show-up
Delay webview fade-in.
2017-08-02 19:08:00 +00:00
Akash Nimare
c3072854fd Merge pull request #258 from zulip/fullscreen-exit-hotkey
notify exit fullscreen shortcut on entering fullscreen mode #247
2017-08-02 19:00:47 +00:00
akashnimare
197e9a0520 styling popup 2017-08-03 00:13:52 +05:30
Akash Nimare
ff84792374 Change font 2017-08-02 22:27:25 +05:30
akashnimare
a0ae29b34a notify exit fullscreen shortcut on entering fullscreen mode #247 2017-08-02 22:15:47 +05:30
Zhongyi Tong
f880683d9c Delay webview fade-in. 2017-08-02 22:56:24 +08:00
Akash Nimare
d80b4a813c setting up Wiki page [WIP] 2017-08-02 18:32:51 +05:30
Akash Nimare
3567f6be6c Setting up wiki page 2017-08-02 18:31:45 +05:30
Akash Nimare
03358b1e50 Added footer in wiki page 2017-08-02 18:04:16 +05:30
Akash Nimare
b098f9e616 Merge pull request #254 from geeeeeeeeek/issue/capitalize-issue/capitalize-menu-items
Capitalize menu items.
2017-08-02 12:27:56 +00:00
Zhongyi Tong
d2fc5bd5e8 Capitalize menu items. 2017-08-02 10:52:35 +08:00
akashnimare
f5e15e3c85 Quit launcher script + app on ctrl+c fixes #253 2017-08-02 04:25:35 +05:30
Greg Price
f7002ecdf3 docs: Briefly describe compiler requirements. 2017-08-01 14:28:54 -07:00
Greg Price
48e5396092 docs: More details about dist output, and small grammar fixes. 2017-08-01 14:24:44 -07:00
akashnimare
6901b5f128 Don't hide titlebar, setting it to default 2017-08-02 02:25:29 +05:30
akashnimare
df0adb373d updated electron to v1.6.11 2017-08-02 02:06:45 +05:30
Greg Price
1c0cf148f8 docs: Revise dev install instructions, especially for Debian/Ubuntu.
This gives an explicit and hopefully-complete set of steps to run
the app from source on a Debian-based system: worked for me on my
Debian "stretch" (the recent release) laptop, though no guarantees
there wasn't some previously-installed package I was relying on.

Also make the generic prerequisites section include everything we
know about, including in particular everything mentioned in the
Debian/Ubuntu section.  Hopefully this is helpful for people working
on other OSes too -- though specific instructions for those would be
very helpful!
2017-08-01 13:06:38 -07:00
akashnimare
c644fa2778 updated gitignore 2017-08-02 00:40:31 +05:30
Akash Nimare
7962ccf19a Merge pull request #241 from geeeeeeeeek/issue/216-webview-focus
Add an os check for webview focus events.
2017-08-01 18:25:55 +00:00
Zhongyi Tong
3ad73a1eaa Explicit focus webview on macOS alone. 2017-08-02 02:23:35 +08:00
Zhongyi Tong
4445baafa9 Add an os check for webview focus events. 2017-08-02 02:07:00 +08:00
Akash Nimare
63cfcbbaf1 Merge pull request #240 from geeeeeeeeek/issue/sidebar-hidden-on-first-use
Set show-sidebar to true by default.
2017-08-01 17:37:07 +00:00
Zhongyi Tong
769972fc4b Set show-sidebar to true by default. 2017-08-02 01:27:45 +08:00
akashnimare
2c8cf2b959 Handle full-reload event properly 2017-08-01 00:52:40 +05:30
Akash Nimare
068fe249ea Merge pull request #236 from geeeeeeeeek/issue/fetch-server-details-from-zulip-api
Issue/fetch server details from zulip api
2017-07-31 18:32:32 +00:00
Akash Nimare
d8c08c1c5b Merge pull request #239 from zulip/windows-path-issue
Fixing server-icon path error [Windows] #181
2017-07-31 18:26:36 +00:00
Zhongyi Tong
56189806a9 Fix a bug that new server form responds to mouse events when hidden. 2017-07-31 18:51:43 +08:00
Zhongyi Tong
0f6e48c65f Remove customized fields of add a server. 2017-07-31 18:17:52 +08:00
akashnimare
fa50243dbb Fixing server-icon path error [Windows]
Since URL of background-image creates path issue on windows, I have implemented the same styling with img src tag which works fine on windows as well.
2017-07-30 17:33:43 +05:30
Akash Nimare
9e4e5e9bfd Merge pull request #237 from zulip/link-issue
Handle new-window event properly #204 #164
2017-07-28 09:38:35 +00:00
Zhongyi Tong
c413a65f07 Make ServerName optional. 2017-07-28 00:06:28 +08:00
Zhongyi Tong
53c91e890a Fetch server details from zulip api. 2017-07-27 23:50:04 +08:00
Akash Nimare
6e3017d5e7 Merge pull request #235 from geeeeeeeeek/issue/reload-app-for-debugging
Add 'Hard Reload' mode for debugging.
2017-07-27 08:52:50 +00:00
Zhongyi Tong
1e57daa8bf Remove background color of action buttons on hover. 2017-07-27 14:16:05 +08:00
Zhongyi Tong
f1ed6695fb Add 'Hard Reload' mode for debugging. 2017-07-27 13:13:17 +08:00
akashnimare
c5d9eceb6d fixing taskbar overlay icon messagecount and styling 2017-07-27 01:39:12 +05:30
Akash Nimare
f2a7ce188d Merge pull request #230 from geeeeeeeeek/feature/collapse-button
Hide/Toggle left sidebar
2017-07-26 18:37:00 +00:00
Akash Nimare
3dd9c89a0e Show sidebar initially
We should not hide it by default since users might not able to find it. Let's show it initially and let them decide.
2017-07-26 23:55:54 +05:30
Akash Nimare
697948c9d8 Merge pull request #219 from geeeeeeeeek/issue/tab-style
Issue/tab style
2017-07-26 15:35:53 +00:00
Zhongyi Tong
5150b7c57c Add menu item for sidebar toggle. 2017-07-26 18:19:08 +08:00
Zhongyi Tong
ce88da3de9 Initialize sidebar appearance from settings. 2017-07-26 18:05:32 +08:00
Zhongyi Tong
0617f41a2d Revert collapse button. 2017-07-26 17:56:58 +08:00
Zhongyi Tong
975bcbbe31 Add settings item for sidebar toggle. 2017-07-26 17:54:03 +08:00
Zhongyi Tong
e44af311ce Snap unread count bubble to the right. 2017-07-26 16:19:03 +08:00
akashnimare
297ada5565 better certificate error message 2017-07-26 01:12:27 +05:30
akashnimare
1fe64cb8d7 Handle new-window event properly #204 #164 2017-07-26 00:35:06 +05:30
Akash Nimare
6557a7d606 Merge pull request #234 from zulip/dev
use relative path for background-image [WIP] #181
2017-07-25 18:27:19 +00:00
akashnimare
9a0f6648ff use relative path for bg-image [WIP] #181 2017-07-23 03:07:20 +05:30
Akash Nimare
3cd4890e60 Merge pull request #233 from zulip/tray
Don't create tray on Windows + macOS
2017-07-22 16:13:25 +00:00
akashnimare
dc287e7e57 Added comment about creating tray on linux 2017-07-22 21:38:32 +05:30
akashnimare
ac0d998804 Linting 2017-07-22 21:19:48 +05:30
akashnimare
1d38ebdf05 Don't create tray form unread counts on Windows + macOS 2017-07-22 21:17:16 +05:30
akashnimare
d70783600d Removed unused checkConnectivity function 2017-07-22 21:04:25 +05:30
akashnimare
527196cdbb Allow only positive position of app [WIP] #231 2017-07-22 20:29:57 +05:30
akashnimare
ffeb960851 left sidebar styling [WIP] 2017-07-22 16:00:45 +05:30
Zhongyi Tong
a36b39ec73 Reduce action button's tooltip size. 2017-07-21 18:06:33 +08:00
Zhongyi Tong
2deb63b557 Add CollapseButton component. 2017-07-21 17:54:53 +08:00
Zhongyi Tong
fcd97f3a32 Add collapse button. 2017-07-21 17:37:28 +08:00
Akash Nimare
67dc1e2a11 Merge pull request #229 from zulip/taskbar-icon
🎉 Added taskbar overlay icon [Windows]
2017-07-21 02:08:59 +00:00
akashnimare
c37ae73392 🎉 Added taskbar overlay icon 2017-07-21 06:42:05 +05:30
akashnimare
9e667e3cb0 Added flashing the taskbar icon #167
on windows, taskbar icon will flash on incoming messages
2017-07-21 05:06:20 +05:30
akashnimare
505fea0e91 Added shortcut to reload full app
useful while debugging
2017-07-21 03:48:14 +05:30
Akash Nimare
70ff8db756 Merge pull request #228 from zulip/dev
Add browser css + hide footer and header links
2017-07-20 18:36:01 +00:00
akashnimare
d6975f7b2a hide header and footer from /login page
we don't want users to navigate these links through app
2017-07-20 23:51:45 +05:30
Zhongyi Tong
6cbea1acba Update style. 2017-07-21 00:45:00 +08:00
Zhongyi Tong
d4decfb6af Remove space in shortcut text on Linux and Windows. 2017-07-20 11:30:49 +08:00
akashnimare
5bbf710529 injecting css in webviews 2017-07-20 01:10:48 +05:30
Zhongyi Tong
b057bffe42 Change to . 2017-07-19 23:47:35 +08:00
akashnimare
4e04c85258 Added basic tooltip 2017-07-18 23:42:04 +05:30
Akash Nimare
bfbd9d4578 Merge pull request #223 from zulip/revert-221-tooltip
Revert "Add tooltip"
2017-07-18 17:35:27 +00:00
Akash Nimare
6594da6ddd Revert "Add tooltip" 2017-07-18 23:04:58 +05:30
Akash Nimare
38abf2deab Merge pull request #221 from zulip/tooltip
Added tooltip
2017-07-18 17:33:11 +00:00
Akash Nimare
0004152e18 Merge branch 'master' into tooltip 2017-07-18 17:26:57 +00:00
akashnimare
e8be57d710 styling tooltip 2017-07-18 16:47:35 +05:30
akashnimare
64e8419410 Added more shortcuts for going back/forward #208 2017-07-18 15:27:07 +05:30
Akash Nimare
f35a3df63b Merge pull request #218 from geeeeeeeeek/feature/silent-reload
Provide silent & seamless reload for the app
2017-07-18 09:41:01 +00:00
Zhongyi Tong
9e15ed2699 Update bubble padding. 2017-07-18 09:21:42 +08:00
Zhongyi Tong
cdc99cda26 Darken shortcut text color. 2017-07-18 09:18:17 +08:00
Zhongyi Tong
7e08af5ced Fix bubble position. 2017-07-18 00:55:58 +08:00
Zhongyi Tong
0ee85bea16 Update style. 2017-07-18 00:47:00 +08:00
Zhongyi Tong
592584fcf4 Use in tab description on win/linux 2017-07-18 00:10:10 +08:00
Zhongyi Tong
8e1b7a0289 Remove empty line. 2017-07-17 23:35:22 +08:00
Zhongyi Tong
715cf8d86f Reload db after changes. 2017-07-17 23:33:01 +08:00
Zhongyi Tong
0dc20cc66c Provide silent & seamless reload for the app. 2017-07-17 23:09:58 +08:00
akashnimare
c860832a73 changed back/forward shortcut to CMD+Left/Right #208 2017-07-15 21:38:17 +05:30
akashnimare
27a29aeba6 change author to Kandra Labs, Inc. 2017-07-14 07:06:46 +05:30
akashnimare
d0bd7e1f1c Allow user to change installation directory fixes #205 2017-07-14 07:00:02 +05:30
akashnimare
72974b075f Add tooltip [WIP] #207 2017-07-14 05:58:20 +05:30
akashnimare
947bab657f Added Menu item History for back+forward #208 2017-07-14 03:29:17 +05:30
akashnimare
5a2975ca4d handle certs error properly [WIP] #211 2017-07-13 22:01:20 +05:30
Akash Nimare
499743e99d Merge pull request #206 from gnprice/new
settings: Tweak text of initial screen to be a bit clearer.
2017-07-13 02:26:42 +05:30
Greg Price
6bd4c44893 settings: Tweak text of initial screen to be a bit clearer.
The label "New Server" sounds to me like it's suggesting I create
a new actual server -- which I don't want to do, I want to connect
to an existing server where a community I know is talking.
Change the text to track a bit closer to what's really going on.

I think there's probably more that would be useful to do to
make this flow smoother for a new user, but this is a start.
2017-07-12 13:51:00 -07:00
akashnimare
5b82a82313 help >> https://zulipchat.com/help 2017-07-13 02:07:04 +05:30
akashnimare
0208e407f0 Added publisher name [Windows] 2017-07-13 00:47:05 +05:30
akashnimare
e3a622fc07 🎉 v1.2.0-beta 2017-07-12 15:57:56 +05:30
akashnimare
f39839618d open /apps, /api, /integration page in browser instead of app 2017-07-12 15:12:58 +05:30
akashnimare
3f93a3346f Allow server which is signed by root cert WIP #150
App used to throw an error because it assumes that the certificate is invalid or not signed properly.
The solution of this problem is same as self-signed certificate fix which is we simply show a warning dialog asking user if they trust the certificate.
2017-07-12 00:45:44 +05:30
Akash Nimare
9ab75b9800 Merge pull request #202 from geeeeeeeeek/feature/save-server-icon
Save server icon && Provide keyboard shortcut to switch between servers.
2017-07-11 10:30:09 -07:00
Zhongyi Tong
f600c4db0e Eliminate the race condition of pipe and reload. 2017-07-11 11:57:17 +08:00
Zhongyi Tong
4e5816697e Enhance the error handling of icon download. 2017-07-11 11:34:51 +08:00
Zhongyi Tong
dc3c446a46 Put local shortcuts back. 2017-07-11 11:12:39 +08:00
Zhongyi Tong
4cd8efa396 Fix linting errors. 2017-07-11 00:54:51 +08:00
Zhongyi Tong
541ba335ae Save server icon to a local folder. 2017-07-11 00:47:30 +08:00
Zhongyi Tong
3e226400c4 Unregister local shortcuts after reload. 2017-07-10 22:37:22 +08:00
Zhongyi Tong
890d7caea5 Provide keyboard shortcut to switch between servers. 2017-07-10 14:40:02 +08:00
akashnimare
45e7993d0c destroy tray on reload 2017-07-10 03:24:28 +05:30
Akash Nimare
8210a7c472 Merge pull request #200 from geeeeeeeeek/feature/move-add-server-button-up
Add the ability to open Settings with specific view
2017-07-09 14:49:50 -07:00
Zhongyi Tong
9e6bb1b48f Fix stying. 2017-07-09 23:08:59 +08:00
Zhongyi Tong
d3c2da7961 Support switching Settings view from different action buttons. 2017-07-09 23:01:46 +08:00
Zhongyi Tong
a5017456f2 Move add server buttom to above area. 2017-07-09 20:29:40 +08:00
akashnimare
76cd62d0c8 Added desktop notification support on windows 8 fixes #199 2017-07-08 04:43:16 +05:30
akashnimare
6b68217494 Unregister shortcut on window close
this will make sure that app's keyboard shortcut such as CTRL+R etc won't interfare
2017-07-08 03:06:32 +05:30
akashnimare
8148d83448 Add sound settings [WIP] #186 2017-07-07 00:08:14 +05:30
akashnimare
a55cda3b1f Added a warning dialog for deleting server 2017-07-06 19:23:35 +05:30
akashnimare
e381960206 hide Existing servers if there is no server 2017-07-06 00:17:12 +05:30
Akash Nimare
091b641abb Merge pull request #197 from zulip/dev
Add beta update option in settings page #192
2017-07-05 08:33:25 -07:00
akashnimare
af0ec80998 Refinements on settings template 2017-07-05 20:56:07 +05:30
akashnimare
5653a38d9b init betaupdates to false 2017-07-05 18:26:04 +05:30
Akash Nimare
01ea3beb99 Merge pull request #196 from geeeeeeeeek/dev
Reload db after update.
2017-07-05 05:53:01 -07:00
Zhongyi Tong
cd387aaf9c Reload db after update. 2017-07-05 20:41:01 +08:00
akashnimare
24b5e0412b changed betaUpdate >> BetaUpdate 2017-07-05 17:15:38 +05:30
Akash Nimare
8174f7b37e Merge pull request #195 from geeeeeeeeek/dev
Change config filename to avoid conflict.
2017-07-05 04:31:48 -07:00
Zhongyi Tong
d3a5eceaf9 Change config filename to avoid conflict. 2017-07-05 19:25:34 +08:00
Akash Nimare
31f285eba9 Merge pull request #194 from geeeeeeeeek/dev
Fix defaultValue not set on the first time.
2017-07-05 04:19:44 -07:00
Zhongyi Tong
b1365f9669 Fix defaultValue not set on the first time. 2017-07-05 18:58:25 +08:00
akashnimare
21351125fa update betaupdate settings 2017-07-05 16:00:43 +05:30
Akash Nimare
50b9ec7220 Merge pull request #193 from geeeeeeeeek/dev
Make ConfigUtil runnable in both processes.
2017-07-05 03:16:54 -07:00
Zhongyi Tong
46cf5c9a5c Make ConfigUtil runnable in both processes. 2017-07-05 18:05:50 +08:00
akashnimare
35a42f3873 Allow auto update for prerelease based on betaupdates setting 2017-07-05 03:12:01 +05:30
akashnimare
14b9fcf8d7 add settings for beta updates [WIP] #173 2017-07-05 03:05:02 +05:30
Akash Nimare
47cfe86a06 Merge pull request #190 from geeeeeeeeek/feature/settings-page
Feature/settings page
2017-07-02 11:43:14 -07:00
Zhongyi Tong
57ad2d63e0 Fix a styling bug. 2017-07-03 02:11:21 +08:00
Zhongyi Tong
2fcc5d9649 Fix linting errors. 2017-07-03 02:02:43 +08:00
Zhongyi Tong
45523f41aa Add tray options in settings page. 2017-07-03 02:02:43 +08:00
Zhongyi Tong
15b3af7b97 Update tray script to support shown/hidden state on start-up. 2017-07-03 02:02:43 +08:00
Zhongyi Tong
81d3aa8a1b Add ConfigUtil to manage config items. 2017-07-03 02:02:43 +08:00
Zhongyi Tong
321860a232 Disable preload script in functional tab. 2017-07-03 02:02:43 +08:00
Zhongyi Tong
1c290fc2cd Add the ability to forward message from renderer to renderer. 2017-07-03 02:02:43 +08:00
Zhongyi Tong
a74b17b989 Finish the framework of settings. 2017-07-03 02:02:43 +08:00
Zhongyi Tong
ffba6b68f8 Extract Nav from Preference. 2017-07-03 02:02:43 +08:00
akashnimare
b553b29328 ignore docs folder while building 2017-07-01 20:56:22 +05:30
akashnimare
d7b44b23d1 add new release checklist [WIP] 2017-07-01 20:54:52 +05:30
akashnimare
b991fac136 destroy multiple tray icons on reload - fixes #191 2017-07-01 12:22:59 +05:30
Akash Nimare
cda8aa3b09 Merge pull request #189 from geeeeeeeeek/issue/close-settings-bug
Fix close settings causing split webviews (#188).
2017-06-28 08:48:06 -07:00
Zhongyi Tong
1e60643ae9 Fix multiple settings tabs. 2017-06-28 20:37:31 +08:00
Zhongyi Tong
d9fbcaf38e Fix linting errors. 2017-06-28 00:47:42 +08:00
Zhongyi Tong
9e5a67c36b Fix close settings causing split webviews (#188). 2017-06-28 00:44:23 +08:00
akashnimare
a16181be33 🎉 v1.1.0-beta 2017-06-23 17:39:15 +05:30
akashnimare
cd0a7741b1 styling save-server-action button 2017-06-23 17:37:28 +05:30
akashnimare
6da523a18b Remove checkbox and redesign save-server-action 2017-06-23 17:24:37 +05:30
akashnimare
86302308a9 Enable quit app using CTRL+Q on windows as well 2017-06-22 02:44:34 +05:30
akashnimare
b0294db133 Allow global return + use script as sourceType 2017-06-22 00:53:35 +05:30
akashnimare
252586cf71 fix module error #185 2017-06-22 00:52:29 +05:30
Akash Nimare
52ea1a48fd Merge pull request #184 from geeeeeeeeek/issue/network-error-page
Improve network error display.
2017-06-21 06:55:55 -07:00
Zhongyi Tong
98e73f807c Reload the app when users try to resume the connection. 2017-06-21 20:47:40 +08:00
Zhongyi Tong
f96dd6e6bc Fix linting error. 2017-06-21 20:11:58 +08:00
Zhongyi Tong
1511ce4610 Reconnect app when network comes back. 2017-06-21 20:10:58 +08:00
Akash Nimare
c2db6fc0f0 updated multi team screenshot 2017-06-21 17:35:58 +05:30
Zhongyi Tong
937a193a61 Fix linting errors. 2017-06-21 15:59:09 +08:00
Zhongyi Tong
3823ac7f78 Improve network error display. 2017-06-21 15:56:05 +08:00
akashnimare
878cc3fe82 focus window onclicking tray menus #fixes #183 2017-06-20 15:19:57 +05:30
akashnimare
eace637f29 Toggle window on clicking tray icon [Windows only] 2017-06-20 15:00:43 +05:30
akashnimare
316b5fda9e ignore spellchecker for input field 2017-06-19 15:56:06 +05:30
akashnimare
e3b8d5ea2a 🎉 v1.0.0-beta 2017-06-19 13:40:36 +05:30
akashnimare
c8434cfd21 disable auto updates for pre-release 2017-06-19 13:32:33 +05:30
akashnimare
fbc048e8cb increased min. height of window 2017-06-19 13:19:28 +05:30
akashnimare
8a4483da80 sidebar styling 2017-06-19 13:06:01 +05:30
akashnimare
8252c9ae6c remove unused tray option 2017-06-19 12:45:33 +05:30
Akash Nimare
5beb425f1c Merge pull request #180 from geeeeeeeeek/issue/refinements-on-server-side
Refinements on the main process
2017-06-19 12:28:04 +05:30
Zhongyi Tong
44337dd04c Fix a bug which would cause WebView not shown after loading. 2017-06-19 00:32:24 +08:00
Zhongyi Tong
bb76e2c2f4 Fix isInternal and open image in browser. 2017-06-18 09:58:13 +08:00
Zhongyi Tong
71305bca4e Load default icon locally. 2017-06-18 09:56:28 +08:00
Zhongyi Tong
9cff5c5a4d Fix linting errors. 2017-06-18 02:28:50 +08:00
Zhongyi Tong
f3cf2229c6 Add the ability to close for functional tabs. 2017-06-18 02:17:18 +08:00
Zhongyi Tong
a62fc3d3bf Split Tab into ServerTab and FuntionalTab. 2017-06-18 00:38:43 +08:00
Zhongyi Tong
e538543512 Move About page into WebView. 2017-06-17 22:38:24 +08:00
Zhongyi Tong
f85bca9879 Save. 2017-06-17 22:38:24 +08:00
Zhongyi Tong
f548a0ae53 Change the way utils load. 2017-06-17 22:38:24 +08:00
Zhongyi Tong
7192dc69f6 Put LinkUtil to renderer-side. 2017-06-17 22:38:23 +08:00
Zhongyi Tong
47eec89a9b Remove debug-purpose-comments in appUpdater. 2017-06-17 22:38:23 +08:00
Akash Nimare
48ff506344 Merge pull request #179 from zulip/window-close-fix
Fixed window close/hide logic + keep app running in background on close
2017-06-16 09:21:34 -07:00
akashnimare
c2e6e9603f fixed hide/quit logic #169, #160 2017-06-16 21:45:45 +05:30
akashnimare
f4f4836887 add focus option in tray + handle quit event properly 2017-06-16 20:39:16 +05:30
akashnimare
02bc5e41a5 keep app running in background on close 2017-06-16 20:29:16 +05:30
Akash Nimare
68c90434b3 Merge pull request #168 from geeeeeeeeek/issue/refinements-on-multi-tab-view
Refinements on multi tab view
2017-06-16 06:41:23 -07:00
Zhongyi Tong
7f8d933ab7 Fix lint errors. 2017-06-16 21:07:48 +08:00
Akash Nimare
935b37705a Merge pull request #175 from veeloinc/package-whitespace
normalize whitespace
2017-06-13 14:50:21 -07:00
Kevin Turner
798235fb06 normalize whitespace 2017-06-13 08:26:38 -07:00
Zhongyi Tong
743d689281 Avoid multiple webviews show at the same time. 2017-06-13 02:36:47 +08:00
Zhongyi Tong
4c188bbdc8 Update tabs position. 2017-06-13 02:18:35 +08:00
Zhongyi Tong
26e0543ae2 Fix missing badge element. 2017-06-13 01:55:53 +08:00
Zhongyi Tong
463701c5f8 Fix user-agent. 2017-06-13 01:50:37 +08:00
Zhongyi Tong
4c33f0779c Update badge style when unread number too large. 2017-06-13 00:57:28 +08:00
Zhongyi Tong
4888efb9f2 Use Zulip as app title. 2017-06-13 00:57:28 +08:00
Zhongyi Tong
ea332a9ff3 Rebase with geeeeeeeeek/issue/network-disconnectivity-resolution. 2017-06-13 00:57:28 +08:00
Zhongyi Tong
f1b2fdcf99 Disable WebView:focus outline. 2017-06-13 00:57:27 +08:00
Zhongyi Tong
9f73160f74 Add the ability to show badge count for each server. 2017-06-13 00:57:27 +08:00
Zhongyi Tong
b18e3ad5d2 Update title and badge on title change. 2017-06-13 00:57:27 +08:00
Zhongyi Tong
ed6013fb5d Add the ability to reload active webview in UI. 2017-06-13 00:57:27 +08:00
Zhongyi Tong
39b436819c Componentize Tab. 2017-06-13 00:57:27 +08:00
Zhongyi Tong
3e74fc9b0a Finish the refactoring of WebView. 2017-06-13 00:57:25 +08:00
Zhongyi Tong
0708519816 Componentize WebView. 2017-06-13 00:56:36 +08:00
Zhongyi Tong
84808313fe Add the ability to open DevTools for active webview. 2017-06-13 00:51:44 +08:00
Zhongyi Tong
e7e55596c6 Update icon names. 2017-06-13 00:51:44 +08:00
Zhongyi Tong
1b8eb099ab Disable text selections in multi-tab view. 2017-06-13 00:51:44 +08:00
Zhongyi Tong
1fa276a400 Update css structure. 2017-06-13 00:51:44 +08:00
Zhongyi Tong
0751f6ac72 Remove obsoleted main.css and rename servermanager.css to main.css.
Since we adopt new multi-tab UI for the app, the old main.css was obsoleted and not in use. For clarity, We should change the name of main.js's css file to main.css.
2017-06-13 00:51:44 +08:00
Akash Nimare
3d8da55648 update node dependency 2017-06-12 17:05:51 +05:30
Akash Nimare
318a729a4a Merge pull request #174 from geeeeeeeeek/issue/set-ua-properly
Set user-agent from a singleton util.
2017-06-12 02:27:09 -07:00
Zhongyi Tong
aab581f204 Fix missing app version in user-agent. 2017-06-12 11:32:23 +08:00
Zhongyi Tong
c0075b4f1c Set user-agent from a singleton util. 2017-06-12 01:07:45 +08:00
akashnimare
7bd2e751c5 fixed linting 2017-06-10 15:39:05 +05:30
akashnimare
5ff2492c79 temporarily disable electron browserwindow and other tests
Because of the chromedriver issue other tests like linting is failing. Disablingthis particular test will allow us to test for linting errors. This is just a temporary workaround and will be fixed ASAP.
2017-06-10 15:11:25 +05:30
Akash Nimare
a26df708f4 Merge pull request #171 from zulip/dev
Set useragent correctly fixes #170
2017-06-09 20:54:25 +05:30
akashnimare
57df256a4a set useragent correctly [WIP] #170 2017-06-09 07:52:45 +05:30
Akash Nimare
7f1890d8a1 Merge pull request #165 from geeeeeeeeek/issue/network-disconnectivity-resolution
Issue/network disconnectivity resolution
2017-06-04 01:38:12 -07:00
Akash Nimare
ff12b041a1 Merge pull request #162 from geeeeeeeeek/master
Fix WebView resize problem
2017-06-01 10:33:25 -07:00
Zhongyi Tong
606e407aee Fix network disconnectivity issue. 2017-06-01 19:33:56 +08:00
Zhongyi Tong
b0db81095a Attach user-agent to webview requests. 2017-06-01 19:09:36 +08:00
Zhongyi Tong
2208b03612 Remove vscode conf. 2017-05-30 20:41:13 +08:00
Zhongyi Tong
7cf2422d76 Fix webview resize problem when switching back to that webview. 2017-05-30 20:32:55 +08:00
akashnimare
57d4e5c930 load icons locally fixes #152 2017-05-28 02:27:25 -07:00
Akash Nimare
deafa315df Merge pull request #159 from zulip/bugfix-#158-focus-webview-on-tab-context-switch
Bugfix #158 focus webview on tab app switch
2017-05-28 01:29:55 -07:00
simplyahmazing
4c09da791c remove unneeded listener 2017-05-28 04:22:20 -04:00
simplyahmazing
e3490dbfa5 makes webview focus when browser window gains focus 2017-05-28 04:20:54 -04:00
akashnimare
f6358a06fd fixing network issue #157 [WIP] 2017-05-25 15:50:15 -07:00
akashnimare
3ef346495f updated spectron dependency 2017-05-25 11:48:49 -07:00
Akash Nimare
64fcc51c7e add chai as dependency 2017-05-24 18:59:06 -07:00
Akash Nimare
a5299a6973 Merge pull request #156 from zulip/20170523-090000-multiple-organizations-test
commment test
2017-05-24 18:53:42 -07:00
Akash Nimare
154aa323ab Update README.md 2017-05-24 18:48:28 -07:00
simplyahmazing
c52318f5d6 add chai as a dep 2017-05-24 21:41:25 -04:00
Akash Nimare
bcd4048709 Merge pull request #155 from lonerz/patch-2
CONTRIBUTING.md: Fix styling issues.
2017-05-24 18:28:45 -07:00
Joshua Pan
f6cb262d4c CONTRIBUTING.md: Fix styling issues.
beta please
2017-05-24 18:27:44 -07:00
Akash Nimare
ca21912374 Merge pull request #154 from lonerz/patch-1
development.md: Fix some grammar issues.
2017-05-24 18:13:38 -07:00
Joshua Pan
472ba9a199 development.md: Fix some grammar issues. 2017-05-24 17:20:58 -07:00
akashnimare
f3a4d4225d :memo updating development guide [WIP] 2017-05-24 17:16:28 -07:00
Akash Nimare
62178b6035 Update development.md 2017-05-24 17:13:19 -07:00
Akash Nimare
2068ac8905 Update development.md 2017-05-24 16:59:38 -07:00
akashnimare
51352be1f6 :memo updating development guide [WIP] 2017-05-24 16:58:27 -07:00
akashnimare
8c3bfcbdd5 :memo docs updated [WIP] 2017-05-24 16:51:14 -07:00
Akash Nimare
06ed522714 Update development.md 2017-05-24 16:46:44 -07:00
akashnimare
d5526944fe :memo updated development guide [WIP] 2017-05-24 16:44:07 -07:00
akashnimare
616ed89d90 :memo docs updated [WIP] 2017-05-24 16:38:09 -07:00
akashnimare
92362653d3 :memo docs updated [WIP] 2017-05-24 16:07:26 -07:00
Akash Nimare
06a09574a5 Update README.md 2017-05-24 15:27:01 -07:00
Akash Nimare
c19a7f81c2 updated development guide [WIP] 2017-05-24 15:26:14 -07:00
Akash Nimare
635b6f6128 📝 docs improvements [WIP] 2017-05-24 15:21:12 -07:00
Akash Nimare
996084cd36 Docs improvement [WIP] 2017-05-24 12:27:37 -07:00
Akash Nimare
6a403e52e1 Improve docs [WIP] 2017-05-24 11:20:02 -07:00
Akash Nimare
aed58ed1a8 Update README.md 2017-05-24 10:52:56 -07:00
Akash Nimare
0412702e35 improve docs [WIP] 2017-05-24 10:52:34 -07:00
Akash Nimare
cf0086e324 📝 improving docs 2017-05-24 10:31:06 -07:00
Akash Nimare
d156ba99c8 📝 improving docs 2017-05-24 10:30:54 -07:00
akashnimare
7d71e2e04d add missing dependency 2017-05-23 19:41:14 -07:00
Akash Nimare
546abb28c0 Merge pull request #153 from zulip/20170523-090000-multiple-organizations-test
multiple organizations tests
2017-05-23 19:37:23 -07:00
simplyahmazing
a43d008aa9 commment test 2017-05-23 22:15:10 -04:00
simplyahmazing
777ed5c561 cleans up tests 2017-05-23 22:11:22 -04:00
simplyahmazing
61db04c574 Merge branch 'master' into 20170523-090000-multiple-organizations-test 2017-05-23 20:33:01 -04:00
simplyahmazing
93123401fe fixes test 2017-05-23 20:32:53 -04:00
simplyahmazing
dca4debee2 setting up multi org test 2017-05-23 20:31:51 -04:00
akashnimare
bef1503b9e 💄 styling setting sidebar 2017-05-23 13:01:40 -07:00
simplyahmazing
f9d430b2d2 sets chai promised tests; improves application launched test case 2017-05-23 13:44:09 -04:00
simplyahmazing
0d551861c7 fixes tests; fixes linting errors 2017-05-23 13:09:58 -04:00
simplyahmazing
4ced663f01 removes window frame 2017-05-23 12:42:16 -04:00
Akash Nimare
06f15eba2d Merge pull request #151 from zulip/140-fixes
💥 Added multiple server support feature
2017-05-22 18:53:21 -07:00
Akash Nimare
3008cd1f24 Merge pull request #140 from geeeeeeeeek/feature/#1-add-support-for-multiple-server
🎉  Add nice support for multiple Zulip servers
2017-05-22 18:43:20 -07:00
SimplyAhmazing
ea449965af Merge branch '140-fixes' into feature/#1-add-support-for-multiple-server 2017-05-22 18:42:24 -07:00
simplyahmazing
06ecdb678c focus webview after loading 2017-05-22 21:21:53 -04:00
simplyahmazing
dd99560426 removes commented setting 2017-05-22 20:41:24 -04:00
simplyahmazing
cebea28ba8 change badge count on organization nav change 2017-05-22 20:37:48 -04:00
Akash Nimare
da3e7e39b9 shrink left sidebar
Just to remove unnecessary spacing.
2017-05-22 17:09:49 -07:00
Zhongyi Tong
4b4e3a3d01 Update Save Server button text. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
aff4632927 Fix a typo. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
eeb9ee512f Enable asar. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
11785d78d2 Fix broken path issues using the module after packing. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
f8d8d0ce2e Fix test errors. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
b5af5d413d Add back badge count feature. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
d962bd6e60 Migrate /domain to /domains. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
0271ada591 Fix linter warnings. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
42fedf2d73 Add reload button to preference page. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
0b17dbb014 Initialize domains on first use. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
ae3c595d82 Integrate actions from menu and tray with webview. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
de34a22740 Move methods from mainWindow.webContents (index.js) to WebView (main.js). 2017-05-22 17:09:49 -07:00
Zhongyi Tong
865553fa45 Redirect to server settings page when domains are empty. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
23fd7ba2b3 Update DomainUtil and finish PreferenceView. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
e43b651060 Set up layout for PreferenceView. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
c0fc7718aa Use multiple webview to increase loading speed and allow servers to be online at the same time. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
3fe23e84b3 Refactor ServerManagerView and setup layout for PreferenceView. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
6b29139805 Finish interactions of switching servers in ServerManagerView. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
0bfa202763 Change domain config schema and update DomainUtil. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
8c494f329b Setup the layout for ServerManagerView. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
35bf2b0012 Add DomainUtil to manage domains in one place. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
5ac4ea71c9 Update prototype design. 2017-05-22 17:09:49 -07:00
Zhongyi Tong
f7eb4128cb Finish UI template of multi-server manager. 2017-05-22 17:09:49 -07:00
simplyahmazing
f9f21cd626 bring back electron frame; shrink organization switcher bar 2017-05-22 19:16:05 -04:00
simplyahmazing
4a84f17d86 merge usptream master 2017-05-22 19:07:01 -04:00
akashnimare
d5a92110db build for mac apple store 2017-05-22 15:31:03 -07:00
akashnimare
e9cf591559 add mas config 2017-05-22 14:57:33 -07:00
akashnimare
ba60c04452 🆙 update dependencies 2017-05-22 12:10:58 -07:00
simplyahmazing
f4567c762d update spellchecker and electron to latest 2017-05-22 14:38:21 -04:00
Akash Nimare
b2aec4b27e Downgrade electron 2017-05-22 10:16:52 -07:00
akashnimare
0fc58b4cc9 Enable zipping of linux installers 2017-05-21 17:14:14 -07:00
akashnimare
e9d7bfe48b bump electron to v1.6.8 2017-05-21 17:05:36 -07:00
Akash Nimare
070eb099d0 typo fixed 2017-05-19 16:13:25 -07:00
Akash Nimare
e35661993d Document windows installer #148 2017-05-19 16:12:34 -07:00
Akash Nimare
f6234cd2f6 Merge pull request #147 from Lplenka/sleepresume
[WIP]#143 Improve handling of coming back online with the desktop app
2017-05-19 05:26:04 +05:30
Lplenka
66475cf46c [WIP] #143 Fixed lint error 2017-05-04 16:39:19 +05:30
Lplenka
06734d2e56 [WIP]#143 powerMoniter now checks when app is resumed 2017-05-04 15:12:15 +05:30
Zhongyi Tong
1c012c7a28 Update Save Server button text. 2017-04-30 23:08:45 +08:00
Zhongyi Tong
7fdccd278b Fix a typo. 2017-04-30 01:14:42 +08:00
Zhongyi Tong
9a4556a59c Enable asar. 2017-04-30 01:06:05 +08:00
Zhongyi Tong
92b9388f9a Fix broken path issues using the module after packing. 2017-04-30 00:01:22 +08:00
Zhongyi Tong
869600e3d4 Fix test errors. 2017-04-29 19:56:39 +08:00
Zhongyi Tong
3c22d5462d Add back badge count feature. 2017-04-29 17:37:08 +08:00
Zhongyi Tong
92ff92f501 Migrate /domain to /domains. 2017-04-29 15:37:02 +08:00
Zhongyi Tong
47b3dd04fb Fix linter warnings. 2017-04-29 01:10:21 +08:00
Zhongyi Tong
9ed09c9e1c Add reload button to preference page. 2017-04-28 23:56:59 +08:00
Zhongyi Tong
239cce8a4c Initialize domains on first use. 2017-04-28 23:56:59 +08:00
Zhongyi Tong
d6a35408b8 Integrate actions from menu and tray with webview. 2017-04-28 23:56:59 +08:00
Zhongyi Tong
35e6f7dcdd Move methods from mainWindow.webContents (index.js) to WebView (main.js). 2017-04-28 23:56:56 +08:00
Zhongyi Tong
a38c933bc8 Redirect to server settings page when domains are empty. 2017-04-28 23:00:08 +08:00
Zhongyi Tong
720ccf5d00 Update DomainUtil and finish PreferenceView. 2017-04-28 23:00:08 +08:00
Zhongyi Tong
8dc87d0485 Set up layout for PreferenceView. 2017-04-28 23:00:08 +08:00
Zhongyi Tong
cd9f1c0c47 Use multiple webview to increase loading speed and allow servers to be online at the same time. 2017-04-28 23:00:08 +08:00
Zhongyi Tong
331452edbb Refactor ServerManagerView and setup layout for PreferenceView. 2017-04-28 23:00:08 +08:00
Zhongyi Tong
d18885ecc9 Finish interactions of switching servers in ServerManagerView. 2017-04-28 23:00:08 +08:00
Zhongyi Tong
1aef53ef94 Change domain config schema and update DomainUtil. 2017-04-28 23:00:08 +08:00
Zhongyi Tong
11086210de Setup the layout for ServerManagerView. 2017-04-28 23:00:08 +08:00
Zhongyi Tong
e0d693fa19 Add DomainUtil to manage domains in one place. 2017-04-28 23:00:08 +08:00
Zhongyi Tong
81c71b1f83 Update prototype design. 2017-04-28 23:00:07 +08:00
Zhongyi Tong
cf8c83e3cf Finish UI template of multi-server manager. 2017-04-28 23:00:07 +08:00
Akash Nimare
3dade768a7 Merge pull request #141 from Lplenka/Certificate_Issue
 Added support for self-signed certificate fixes #126
2017-04-28 02:46:18 +05:30
Lalu Prasad Lenka
7b3c7ba5fa Merge branch 'master' into Certificate_Issue 2017-04-28 02:28:12 +05:30
Akash Nimare
616bc0f73b Merge pull request #144 from Lplenka/offline
Fixed internet connectivity error on Linux
2017-04-28 02:23:46 +05:30
Lplenka
8ecdf1f18a Update PR #115 code clean-up 2017-04-28 01:50:23 +05:30
Lplenka
d2daa65059 Update PR #115 Added ERR_NAME_NOT_RESOLVED condition 2017-04-28 00:38:55 +05:30
Lplenka
487ee538e3 Lint error fix 🔧 2017-04-27 12:54:31 +05:30
Lplenka
6382c6d2b3 Improved PR #115 2017-04-27 12:50:53 +05:30
Lplenka
cbcff67d28 [WIP] issue #126, now not remembering servers 2017-04-21 18:39:58 +05:30
akashnimare
61a429365b support self-signed cert fixing #126 [WIP] 2017-04-18 04:57:33 +05:30
akashnimare
e55f38a962 🚚 moved files to renderer 2017-04-18 04:17:32 +05:30
akashnimare
81798583ae unused tray.js from main process deleted + travis test 2017-04-18 04:04:15 +05:30
akashnimare
9cab61cebc 🎉 bump v0.5.10 2017-04-18 00:40:22 +05:30
Akash Nimare
f290732cb6 Merge pull request #134 from Lplenka/master
Added unread counts in tray icon Fixes #110
2017-04-18 00:26:36 +05:30
Lplenka
2dd44852fa Lint error fixed #126[WIP] 2017-04-17 14:22:30 +05:30
Lplenka
bd2f17deec Removed Error from console #126[WIP] 2017-04-17 12:55:41 +05:30
Lplenka
06faf46bcc Support for Switching to server with certificate error #126[WIP] 2017-04-17 12:39:54 +05:30
Lplenka
468e9d539b issue #126 [WIP] 2017-04-17 12:15:07 +05:30
akashnimare
449f407236 Enable cache on travis 2017-04-17 00:20:51 +05:30
Lplenka
7efe90e709 🔧 Lint Error fixed 2017-04-14 09:16:09 +05:30
Lalu Prasad Lenka
184e1a5bc4 Merge branch 'master' into master 2017-04-14 08:20:29 +05:30
Akash Nimare
85e7b337a7 Fixing travis test [WIP] 2017-04-14 06:37:08 +05:30
akashnimare
83759bde1c using known working version for devdependencies 2017-04-14 05:11:51 +05:30
akashnimare
d9b1d45e0e 🔧 fix linting errors 2017-04-14 04:54:44 +05:30
Akash Nimare
df91c20f36 Merge pull request #137 from brockwhittaker/landing-page-redesign
Redesign the landing page to look more inline with brand design.
2017-04-14 04:30:26 +05:30
brockwhittaker
c1f6159d69 Redesign the landing page to look more inline with brand design.
This redesigns the landing page to incorporate the Zulip style guide.
This also adds two animations:

1. The error text slides down on error and back up when typing.
2. The form section shakes when there is invalid input for 0.5s.
2017-04-13 12:03:45 -07:00
Akash Nimare
5a0461211a remove typo 2017-04-13 01:38:41 +05:30
Akash Nimare
1394f790c3 Merge pull request #136 from brockwhittaker/master
about: Restyle Electron => Zulip desktop page.
2017-04-13 01:06:05 +05:30
brockwhittaker
4d374ff40c Restyle Electron => Zulip desktop page.
This restyles the "about" page to look slicker and have brand colors.
2017-04-12 12:24:27 -07:00
akashnimare
0d15435408 🔖 v0.5.9 2017-04-11 01:54:39 +05:30
Lalu Prasad Lenka
d4448ba086 Merge branch 'master' into master 2017-04-09 10:02:42 +05:30
akashnimare
4bd6fde5b6 remove unused dependency configstore 2017-04-09 02:37:12 +05:30
Akash Nimare
e5097ace06 📝 use nvm to install nodejs #135 2017-04-05 19:48:13 +05:30
Lplenka
ca078cbbfd Fixed size bug in Mac OS #110 [WIP] 2017-04-02 13:29:14 +05:30
Lplenka
6d45105b69 Fixed bug in Mac OS #110 [WIP] 2017-04-02 13:24:56 +05:30
Lplenka
40f81af2dd Lint error fixed #110 [WIP] 2017-04-02 11:06:39 +05:30
Lalu Prasad Lenka
d5e6184e75 Fixed bugs in Windows #110 [WIP] 2017-04-02 05:09:43 +05:30
Lplenka
0ec38ba41d Added function to set tray size according to OS, fixed toggle tray 2017-04-02 09:33:13 +05:30
Lplenka
460a64710a Js Lint error fixed 🔧, Fixes #110 2017-04-02 07:40:29 +05:30
Lplenka
9ec62a748f Added communication b/w Main & Renderer process for proper functioning of Tray icon #110 [WIP] 2017-04-02 01:21:33 +05:30
Lplenka
a7a80cef99 Added Tray.js to renderer js files 2017-04-02 00:55:48 +05:30
akashnimare
bcaf54b349 🔥 remove macOs swipe navigation 2017-04-01 11:19:39 -07:00
akashnimare
1f6d0762bb 🐛 fixing configstore permission issue on windows #125 [WIP] 2017-04-01 10:47:52 -07:00
akashnimare
1aa1655676 🔧 update electron-builder/updater to latest fixes #133 2017-03-31 02:02:47 +05:30
akashnimare
0ad66399d0 🐛 set valid config #133 2017-03-31 01:23:44 +05:30
akashnimare
3a6bb14224 🐛 fixing dmg build failed [WIP] #133 2017-03-31 00:47:26 +05:30
Akash Nimare
53eb8051ad Update CONTRIBUTING.md 2017-03-23 15:45:30 +05:30
Akash Nimare
ed9174f57c Merge pull request #119 from sumedh123/master
updated contributing guide
2017-03-23 15:44:10 +05:30
Akash Nimare
260d6a1906 Updated community section 2017-03-23 15:42:27 +05:30
Akash Nimare
dc53319c8e 🆙 bump electron-spellcheck to v1.0.5 2017-03-21 00:43:10 +05:30
Akash Nimare
77369536b3 Merge pull request #130 from Lplenka/master
Check if zulip server is down Fixes Issue #124
2017-03-20 12:31:18 +05:30
Lplenka
531afcb1e5 Fixed root directory's package.json 2017-03-19 17:33:53 +05:30
Lplenka
d6c4eeccf8 Code formatting done 2017-03-19 01:23:13 +05:30
Lplenka
496b906fd0 Merge branch 'master' of https://github.com/Lplenka/zulip-electron 2017-03-19 01:18:32 +05:30
Lplenka
1be29faea6 Removed spaces from imported/exported varibles & fixed the package.json in app folder 2017-03-19 01:16:07 +05:30
Akash Nimare
bc9a7c9890 update copyright 2017-03-19 00:50:50 +05:30
Lplenka
5745276dbb Code formatting done Fixes #124 2017-03-18 13:20:15 +05:30
Lplenka
acf0282aa0 Checks Server error while switching server 2017-03-18 12:40:38 +05:30
Lplenka
54d942178a Implemented function in index to check server error while loading 2017-03-18 12:32:43 +05:30
Lplenka
d84ada373e Added https node module 2017-03-18 12:22:29 +05:30
Lplenka
d4d36d0582 Removed reductant call to prefDomain() 2017-03-18 12:14:12 +05:30
Lplenka
63e6c634b9 Added Node global symbols to avoid setImmdediate Reference Error 2017-03-18 12:11:45 +05:30
Lplenka
4bc558cdbc Merge branch 'master' of https://github.com/Lplenka/zulip-electron 2017-03-18 09:04:19 +05:30
Cynthia Lin
34293fd66b Enable zulipbot build notifications.
PRs labeled with the *travis updates* label will receive build notifications from zulipbot.
2017-03-18 08:52:52 +05:30
Akash Nimare
5eba4b8200 Merge pull request #129 from synicalsyntax/patch-1
Enable zulipbot build notifications.
2017-03-18 00:48:47 +05:30
Akash Nimare
a949307820 Merge pull request #128 from Lplenka/master
show window when the web page has been rendered and window can be displayed without a visual flash.
2017-03-18 00:28:16 +05:30
Cynthia Lin
a714977b5a Enable zulipbot build notifications.
PRs labeled with the *travis updates* label will receive build notifications from zulipbot.
2017-03-11 11:46:43 -08:00
Lplenka
bfcaa51c46 🐛 🔧 linting error fixed 2017-03-11 01:55:48 +05:30
Lplenka
d7d3017bc1 Bug Fixes 2017-03-11 01:46:35 +05:30
Lalu Prasad Lenka
c7ce8fb7c8 Merge pull request #2 from zulip/master
Merging with zulip-electron origin
2017-03-07 13:42:02 +05:30
Sumedh Nimkarde
65a80de01d update Community section 2017-03-07 07:52:30 +05:30
akashnimare
f6bf210451 🔨 fixed linting errors 2017-03-06 15:18:52 +05:30
Akash Nimare
8c23ec3417 Merge pull request #122 from geeeeeeeeek/feature/#104-add-option-to-hide-tray-icon
#104 Add a menu item to toggle the tray icon.
2017-03-06 15:01:50 +05:30
Akash Nimare
97bbd809f7 Merge pull request #121 from geeeeeeeeek/bugfix/#120-fix-error-launching
#120 Fix error launching caused by missing  keyword.
2017-03-06 14:31:38 +05:30
Akash Nimare
76381cac08 Merge pull request #118 from Lplenka/master
Added localhost support fixes #78
2017-03-06 14:30:29 +05:30
Sumedh Nimkarde
13c4ceedc2 add Community section 2017-03-05 22:47:43 +05:30
Sumedh Nimkarde
f0889edf9c add zulipbot link 2017-03-05 15:38:18 +05:30
Zhongyi Tong
ab8367c946 #104 Add a menu item to toggle the tray icon. 2017-03-05 00:54:55 +08:00
Zhongyi Tong
c5887c8f71 #120 Fix error launching caused by missing keyword. 2017-03-04 23:46:26 +08:00
Sumedh Nimkarde
7b7ab03d0b add Chat section 2017-03-04 11:23:20 +05:30
Lplenka
394caa7934 Issue fix in better way 2017-03-04 10:02:27 +05:30
Lplenka
db2860b53e code formatting done 2017-03-04 01:32:34 +05:30
Lplenka
6d20df3557 Changed error message 2017-03-04 01:01:31 +05:30
Lplenka
2942cd1244 merge conflicts resolved 2017-03-04 00:44:40 +05:30
Lplenka
174049f489 issue #78 fixed 2017-03-04 00:16:41 +05:30
Lalu Prasad Lenka
21eae28999 Merge pull request #1 from zulip/master
Update from Original
2017-03-03 11:04:39 +05:30
akashnimare
f256cbcd5d Added error message when input url is null fixes #116 2017-03-02 17:35:20 +05:30
akashnimare
ff8c20f0b4 Removed unnessary code #116 2017-03-02 17:08:42 +05:30
akashnimare
9e0c17a793 🐛 fixing it in preference window #116 2017-03-02 17:05:01 +05:30
akashnimare
b4fb00aa52 🐛 show error message if input server url is null #116 2017-03-02 16:47:41 +05:30
akashnimare
13d0b5e51c Fixed linting error 2017-03-02 16:00:33 +05:30
Akash Nimare
dc15e4578c Merge pull request #115 from jajodiaraghav/offline
Display an error message if internet not working.
2017-03-02 15:36:19 +05:30
Raghav Jajodia
673da66ee9 Display error message if internet not working.
An error message with 'Try again' option is displayed
if the internet is not working.
Fixes #114
2017-02-28 23:01:13 +05:30
Akash Nimare
e397e9bfb4 Merge pull request #113 from chiragjn/master
Fix issue #112 for displaying Keyboard shortcuts
2017-02-24 14:35:48 +05:30
Chirag Jain
4eca2e9254 Fix lint errors 2017-02-24 11:31:10 +05:30
Chirag Jain
a1407826b6 Fix issue #112 for displaying Keyboard shortcuts 2017-02-24 10:57:29 +05:30
akashnimare
6d8f83798b 📦 update dependencies 2017-02-24 02:34:42 +05:30
akashnimare
bfc03c7e95 🐛 fix useragent + notifications #108 + #111 2017-02-24 02:30:13 +05:30
akashnimare
06737ce629 👌 detect Windows version (7/10) #108 2017-02-21 01:04:24 +05:30
akashnimare
6b09840347 improving usergent #108 2017-02-21 00:29:20 +05:30
akashnimare
19b5eecdcd Check if webview's url is failing travis tests on linux [WIP] #109 2017-02-18 16:51:31 +05:30
akashnimare
f443918433 🔧 added spellchecker dependencies in travis 🐧 2017-02-18 16:10:54 +05:30
akashnimare
bb74e58d63 🎨 fixed linting + #108 2017-02-18 15:56:41 +05:30
akashnimare
612e670bb5 🐛 added app version + os details in useragent #108 2017-02-18 15:17:30 +05:30
akashnimare
34bb55cb9f 🐛 set user-agent to ZulipElectron/version #108 2017-02-17 15:39:06 +05:30
akashnimare
1457e82649 📦 updated dependencies 2017-02-17 00:15:14 +05:30
akashnimare
0bdeaaba18 removed unused dependency dialogs 2017-02-17 00:02:27 +05:30
akashnimare
e2286b6110 🚀 windows build script + nsis web installer 2017-02-14 00:28:06 +05:30
akashnimare
27ba3f3068 removed squirrel events code 2017-02-14 00:22:40 +05:30
akashnimare
9859315fea improve logging in autoupdater[WIP] 2017-02-13 23:54:47 +05:30
akashnimare
73d18dde9e init v0.5.8 + updated dependencies 2017-02-13 20:13:33 +05:30
akashnimare
0372befc5a bump version v0.5.7 2017-02-08 10:37:02 +05:30
akashnimare
063799a053 Updated login page title + placeholder 2017-02-07 18:37:14 +05:30
akashnimare
297b307726 update electron-builder to v13.0.0 2017-02-07 14:20:43 +05:30
akashnimare
deed66973f update electron-builder to v13.0.0 2017-02-07 14:20:17 +05:30
akashnimare
03486e438d remove release name+notes details from autoupdater 2017-02-07 14:17:04 +05:30
Akash Nimare
e113f59aad Bump electron-builder to 12.3.1 2017-02-06 02:30:35 +05:30
Akash Nimare
29ece4824a Merge pull request #105 from zulip/nsis
🎉 switched to NSIS + autoupdates
2017-02-06 01:23:55 +05:30
akashnimare
66c62c55e2 fixed logger undefined error 2017-02-06 01:09:03 +05:30
akashnimare
40852942d2 fixed releaseName undefined error 2017-02-02 16:41:52 +05:30
akashnimare
15c8591691 windows installer >> install per all users 2017-02-02 16:16:32 +05:30
akashnimare
e21902a5e3 🆙 electron-updater to 1.4.1 2017-02-02 15:37:10 +05:30
akashnimare
a5d42e8ccd log whats happening in auto-updater 2017-02-02 12:06:13 +05:30
akashnimare
0923df7250 import autoupdater 2017-02-02 11:52:27 +05:30
akashnimare
37f5258210 updated to electron-builder's latest version 2017-02-02 11:07:31 +05:30
akashnimare
19819f7d48 squirrel startup is not needed for NSIS 2017-01-28 21:21:52 +05:30
akashnimare
40f74cdac2 removed auto updater feed 2017-01-28 13:44:07 +05:30
akashnimare
e4ba3b9721 Added electron-updater dependency autoupdates 2017-01-28 13:21:41 +05:30
akashnimare
9a221585b9 set target to NSIS 2017-01-27 11:23:13 +05:30
Akash Nimare
069a0ff306 Added iconUrl to win.build 2017-01-26 02:10:17 +05:30
akashnimare
3153fb91da 🚀 Added back/forward navigation 2017-01-26 01:30:21 +05:30
Akash Nimare
92d4d27fa8 Added app icon >> windows installer 2017-01-26 00:50:50 +05:30
Akash Nimare
209fc4a65c Added squirrel-windows dependency 2017-01-26 00:47:55 +05:30
Akash Nimare
5e9ecedecd Updated electron-builder to v11.5.1 2017-01-25 23:48:30 +05:30
Akash Nimare
613df32bf1 Updated Prerequisites 2017-01-25 23:03:35 +05:30
Akash Nimare
7606f37695 Merge pull request #102 from zulip/dev
update spellchecker to v1.0.0
2017-01-25 22:55:36 +05:30
akashnimare
a2f758a46b spellchecker v1.0.0 2017-01-24 13:03:05 +05:30
akashnimare
2a477abe5f Added log-out shortcut 2017-01-14 23:43:09 +05:30
akashnimare
5f027820f4 ✏️ small typos fixed 2017-01-14 22:42:28 +05:30
Akash Nimare
9e75861546 Fixing travis test (linux) 2017-01-07 17:50:56 +05:30
akashnimare
4060596474 🎨 fixed linting errors 2017-01-07 17:27:54 +05:30
Akash Nimare
2e5888c8af Merge pull request #96 from jajodiaraghav/add_clear_cache_option
Added Clear-cache option in menu
2017-01-07 17:07:09 +05:30
Raghav Jajodia
03d1188e14 Added clear cache option 2017-01-07 17:00:03 +05:30
Raghav Jajodia
c91b0c209a Added clear cache option 2017-01-07 15:58:19 +05:30
Akash Nimare
531973194c Use stable module version
Using stable version of node modules. 
Using '*' or 'latest' in node modules version could cause unexpected results and could break your code.
2017-01-03 15:14:56 +05:30
akashnimare
4d1face275 Removed electron-context-menu 2017-01-03 14:45:04 +05:30
akashnimare
ca7503f1f0 using electron-builder v10.15.1 2017-01-03 14:42:52 +05:30
Akash Nimare
9c163b4166 Merge pull request #97 from zulip/dev
export spellchecker function
2017-01-02 19:19:11 +05:30
akashnimare
742afb1c09 export spellchecker function 2017-01-02 19:15:08 +05:30
Raghav Jajodia
f9f70f001b Added clear cache option 2017-01-02 13:50:19 +05:30
akashnimare
edf34efd86 Removed unnecessary dep. electron-dl 2016-12-31 22:26:42 +05:30
akashnimare
72ebed95da v0.5.4 2016-12-31 22:17:51 +05:30
Akash Nimare
975a6ab8bf electron-spellchecker
Downgrading since v0.7.1 is not working properly on linux.
2016-12-31 22:06:19 +05:30
akashnimare
3352301b67 :head_bandage: update electron-spellchecker 2016-12-31 20:28:09 +05:30
Akash Nimare
358260f766 Update README.md 2016-12-30 03:14:32 +05:30
Akash Nimare
b58052ce34 Merge pull request #94 from GervaisdeM/launch-script-tweak
rename zulip-electron-updater-and-launcher.sh to
2016-12-29 07:29:16 +05:30
GervaisdeM
de9ad8082b rename zulip-electron-updater-and-launcher.sh to
zulip-electron-launcher.sh

changed `npm upgrade` to `npm install`
2016-12-27 00:39:06 -04:00
akashnimare
d6bf84c821 🆙 Linux app version 2016-12-25 02:32:01 +05:30
Akash Nimare
d5cba4096d Update README.md 2016-12-23 02:40:01 +05:30
Akash Nimare
afa12cc266 Update README.md 2016-12-23 02:38:41 +05:30
Akash Nimare
42ede5e54b typo in email 2016-12-23 02:35:56 +05:30
Akash Nimare
1549db5ce0 Merge pull request #93 from zulip/autoupdates
🎉 Autoupdates for windows
2016-12-23 02:35:01 +05:30
akashnimare
a0de440c2e Unregister shortcuts on will-quit 2016-12-23 02:10:45 +05:30
akashnimare
7e54eb89c2 manage squirrel events 2016-12-23 01:48:22 +05:30
akashnimare
f8e77dfa72 Using squirrel.windows 2016-12-23 01:32:08 +05:30
akashnimare
db6d1f300a adding windows auto-update 2016-12-22 02:30:48 +05:30
Akash Nimare
eac2b92cb6 ⬆️ Test timeout 2016-12-16 15:20:03 +05:30
Akash Nimare
a349e0e4e0 Check linux travis 2016-12-16 14:32:02 +05:30
Akash Nimare
58f35569c8 Added trusty option for linux travis 2016-12-16 03:23:20 +05:30
Akash Nimare
4a9f51aa1b Autoupdate button >> later 2016-12-16 02:01:37 +05:30
Akash Nimare
5bb05906b5 Merge pull request #92 from zulip/dev
Update master with dev branch
2016-12-16 01:59:00 +05:30
akashnimare
f80095d953 Updated travis build script 2016-12-15 20:53:35 +05:30
akashnimare
181803755a fix OSX travis build 2016-12-15 20:39:50 +05:30
akashnimare
ef30cd9624 💚 Fixing CI Build 2016-12-15 20:21:05 +05:30
akashnimare
7c82f41e87 🎨 fixed linting errors 2016-12-15 19:59:12 +05:30
akashnimare
61dfcfc3b1 🐛 unregister keyboard shortcuts 2016-12-15 01:57:20 +05:30
Akash Nimare
e3deb93730 Update Zulip server 2016-12-14 03:16:45 +05:30
Akash Nimare
d6a3e5fe1b Updated zulip server 2016-12-14 03:15:33 +05:30
akashnimare
dc15edf0cd Unsubscribe spellchekcer 2016-12-14 03:02:01 +05:30
akashnimare
e5a60cc077 📦 v0.5.2-Beta 2016-12-14 01:53:00 +05:30
akashnimare
4d5c57fa0b disable auto-updates on dev mode 2016-12-14 01:11:08 +05:30
akashnimare
98bd4fd9b9 🔥 2016-12-14 00:55:29 +05:30
akashnimare
8b808ff9b2 Added auto-updates for OSX 2016-12-14 00:49:30 +05:30
akashnimare
4a2a495738 new tray icons 2016-12-10 14:39:54 +05:30
akashnimare
309816e40a tray icon fixed 2016-12-12 19:15:36 +05:30
Akash Nimare
79a31000df 📝 Updated installation instructions 2016-12-12 18:49:40 +05:30
akashnimare
8410769fdd linux icon fixed 2016-12-10 10:42:48 +05:30
Akash Nimare
fd25ac0cc4 Added Linux app category 2016-11-25 15:08:02 +05:30
154 changed files with 21503 additions and 1421 deletions

5
.gitattributes vendored
View File

@@ -1 +1,4 @@
* text=auto
* text=auto
package-lock.json binary
app/package-lock.json binary

8
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,8 @@
---
<!-- Please Include: -->
- **Operating System**:
- [ ] Windows
- [ ] Linux/Ubutnu
- [ ] macOS
- **Clear steps to reproduce the issue**:
- **Relevant error messages and/or screenshots**:

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

@@ -0,0 +1,16 @@
---
<!--
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

39
.gitignore vendored
View File

@@ -1,9 +1,38 @@
node_modules
npm-debug.log
domain.json
dist
config.gypi
# Dependency directories
node_modules/
# npm cache directory
.npm
# Compiled binary build directory
dist/
#snap generated files
snap/parts
snap/prime
snap/snap
snap/stage
snap/*.snap
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
// osx garbage
*.DS_Store
.DS_Store
# dotenv environment variables file
.env
# miscellaneous
.idea
config.gypi
# Test generated files
tests/package.json
.python-version

1
.node-version Normal file
View File

@@ -0,0 +1 @@
6.9.4

View File

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

View File

@@ -1,37 +1,47 @@
#Contributing Guidelines
# Contributing Guidelines
Thanks for taking the time to contribute!
The following is a set of guidelines for contributing to zulip-electron. These are just guidelines, not rules, use your best judgment and feel free to propose changes to this document in a pull request.
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.
## 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](http://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).
* 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/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 problem, [open a new one](https://github.com/zulip/zulip-electron/issues/new). Please pay attention to following points while opening an 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).
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-Desktop 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.
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 the problem.
Detailed information is very helpful to understand an issue.
For example:
* How to reproduce, step-by-step
* Expected behavior (or what is wrong)
* Screenshots (for GUI issues)
* Application version
* Operating system
* 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 welcome.
Pull Requests are always welcome.
1. When you edit the code, please run `npm run test` to check formatting of your code before git commit.
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:
* Operating System version on which you tested
* 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,80 +1,27 @@
# Zulip Desktop Client
# 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)
[![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)
This is an experimental replacement for the [Zulip Desktop
app](https://github.com/zulip/zulip-desktop) implemented in
[Electron](http://electron.atom.io/).
Desktop client for Zulip. Available for Mac, Linux, and Windows.
The goal is to achieve feature-compatibility with the old desktop app
and then start adding cool features like easy support for
multi-account, auto-updates etc.
<img src="http://i.imgur.com/ChzTq4F.png"/>
## Installation
# Download
Please see the [installation guide](https://zulipchat.com/help/desktop-app-install-guide).
Clone the source locally:
# Features
* Sign in to multiple organizations
* Desktop notifications with inline reply
* Tray/dock integration
* Multi-language spell checker
* Automatic updates
```sh
$ git clone https://github.com/zulip/zulip-electron
$ cd zulip-electron
```
If you're on Debian or Ubuntu, you'll also need to install
`nodejs-legacy`:
# Contribute
Use your package manager to install `npm`.
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).
```sh
$ sudo apt-get install npm nodejs-legacy
```
Install project dependencies:
```sh
$ npm install
```
Start the app:
```sh
$ npm start
```
Start and watch changes
```sh
$ npm run dev
```
# Making a release
To package app into an installer use command:
```
npm run dist
```
It will start the packaging process for operating system you are running this command on. Ready for distribution file (e.g. dmg, windows installer, deb package) will be outputted to `dist` directory.
You can create Windows installer only when running on Windows, the same is true for Linux and OSX. So to generate all three installers you need all three operating systems.
## Features
- [x] Native Notifications
- [x] SpellChecker
- [x] Keyboard shortcuts
Description | Keys
-----------------------| -----------------------
Default shortcuts | <kbd>Cmd/Ctrl</kbd> <kbd>k</kbd>
Change Zulip Server | <kbd>Cmd/Ctrl</kbd> <kbd>,</kbd>
Back | <kbd>Cmd/Ctrl</kbd> <kbd>[</kbd>
Forward | <kbd>Cmd/Ctrl</kbd> <kbd>]</kbd>
- [x] OSX/Win/Linux installer
- [ ] Launch on OS startup
- [ ] Automatic Updates
## 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/)
# License
Released under the [Apache-2.0](./LICENSE) license.

112
app/main/autoupdater.js Normal file
View File

@@ -0,0 +1,112 @@
'use strict';
const { app, dialog, shell } = require('electron');
const { autoUpdater } = require('electron-updater');
const isDev = require('electron-is-dev');
const ConfigUtil = require('./../renderer/js/utils/config-util.js');
function appUpdater(updateFromMenu = false) {
// Don't initiate auto-updates in development
if (isDev) {
return;
}
if (process.platform === 'linux' && !process.env.APPIMAGE) {
const { linuxUpdateNotification } = require('./linuxupdater');
linuxUpdateNotification();
return;
}
let updateAvailable = false;
// Create Logs directory
const LogsDir = `${app.getPath('userData')}/Logs`;
// Log whats happening
const log = require('electron-log');
log.transports.file.file = `${LogsDir}/updates.log`;
log.transports.file.level = 'info';
autoUpdater.logger = log;
// Handle auto updates for beta/pre releases
const isBetaUpdate = ConfigUtil.getConfigItem('betaUpdate');
autoUpdater.allowPrerelease = isBetaUpdate || false;
const eventsListenerRemove = ['update-available', 'update-not-available'];
autoUpdater.on('update-available', info => {
if (updateFromMenu) {
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.'
});
updateAvailable = true;
// This is to prevent removal of 'update-downloaded' and 'error' event listener.
eventsListenerRemove.forEach(event => {
autoUpdater.removeAllListeners(event);
});
}
});
autoUpdater.on('update-not-available', () => {
if (updateFromMenu) {
dialog.showMessageBox({
message: 'No updates available',
detail: `You are running the latest version of Zulip Desktop.\nVersion: ${app.getVersion()}`
});
// Remove all autoUpdator listeners so that next time autoUpdator is manually called these
// listeners don't trigger multiple times.
autoUpdater.removeAllListeners();
}
});
autoUpdater.on('error', error => {
if (updateFromMenu) {
const messageText = (updateAvailable) ? ('Unable to download the updates') : ('Unable to check for updates');
dialog.showMessageBox({
type: 'error',
buttons: ['Manual Download', 'Cancel'],
message: messageText,
detail: (error).toString() + `\n\nThe latest version of Zulip Desktop is available at -\nhttps://zulipchat.com/apps/.\n
Current Version: ${app.getVersion()}`
}, response => {
if (response === 0) {
shell.openExternal('https://zulipchat.com/apps/');
}
});
// Remove all autoUpdator listeners so that next time autoUpdator is manually called these
// listeners don't trigger multiple times.
autoUpdater.removeAllListeners();
}
});
// Ask the user if update is available
// eslint-disable-next-line no-unused-vars
autoUpdater.on('update-downloaded', event => {
// Ask user to update the app
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'
}, response => {
if (response === 0) {
setTimeout(() => {
autoUpdater.quitAndInstall();
// force app to quit. This is just a workaround, ideally autoUpdater.quitAndInstall() should relaunch the app.
app.quit();
}, 1000);
}
});
});
// Init for updates
autoUpdater.checkForUpdates();
}
module.exports = {
appUpdater
};

View File

@@ -1,36 +0,0 @@
const JsonDB = require('node-json-db');
const {app} = require('electron').remote;
const request = require('request');
const ipcRenderer = require('electron').ipcRenderer;
const db = new JsonDB(app.getPath('userData') + '/domain.json', true, true);
const data = db.getData('/');
console.log(data.domain);
// if (data.domain && window.location.href.indexOf(data.domain) === -1) {
// window.location.href = data.domain
// }
// require('electron-connect').client.create();
window.addDomain = function () {
document.getElementById('main').innerHTML = 'checking...';
let newDomain = document.getElementById('url').value;
newDomain = newDomain.replace(/^https?:\/\//, '');
const domain = 'https://' + newDomain;
const checkDomain = domain + '/static/audio/zulip.ogg';
request(checkDomain, (error, response) => {
if (!error && response.statusCode !== 404) {
document.getElementById('main').innerHTML = 'Connect';
db.push('/domain', domain);
ipcRenderer.send('new-domain', domain);
} else {
document.getElementById('main').innerHTML = 'Connect';
document.getElementById('server-status').innerHTML = 'Not a vaild Zulip Server.';
}
});
};

View File

@@ -1,39 +1,36 @@
'use strict';
const path = require('path');
const fs = require('fs');
const electron = require('electron');
const {app} = require('electron');
const electronLocalshortcut = require('electron-localshortcut');
const ipc = require('electron').ipcMain;
const Configstore = require('configstore');
const JsonDB = require('node-json-db');
const tray = require('./tray');
const windowStateKeeper = require('electron-window-state');
const isDev = require('electron-is-dev');
const appMenu = require('./menu');
const {linkIsInternal, skipImages} = require('./link-helper');
const { appUpdater } = require('./autoupdater');
const db = new JsonDB(app.getPath('userData') + '/domain.json', true, true);
const data = db.getData('/');
const { setAutoLaunch } = require('./startup');
// adds debug features like hotkeys for triggering dev tools and reload
require('electron-debug')();
const { app, ipcMain } = electron;
const conf = new Configstore('Zulip-Desktop');
const BadgeSettings = require('./../renderer/js/pages/preference/badge-settings.js');
const ConfigUtil = require('./../renderer/js/utils/config-util.js');
const ProxyUtil = require('./../renderer/js/utils/proxy-util.js');
const { sentryInit } = require('./../renderer/js/utils/sentry-util.js');
// Adds debug features like hotkeys for triggering dev tools and reload
// in development mode
if (isDev) {
require('electron-debug')();
}
// prevent window being garbage collected
// Prevent window being garbage collected
let mainWindow;
let targetLink;
let badgeCount;
let isQuitting = false;
// Load this url in main window
const staticURL = 'file://' + path.join(__dirname, '../renderer', 'index.html');
const targetURL = function () {
if (data.domain === undefined) {
return staticURL;
} else {
return data.domain;
}
};
const mainURL = 'file://' + path.join(__dirname, '../renderer', 'main.html');
const isAlreadyRunning = app.makeSingleInstance(() => {
if (mainWindow) {
@@ -46,14 +43,7 @@ const isAlreadyRunning = app.makeSingleInstance(() => {
});
if (isAlreadyRunning) {
app.quit();
}
function checkWindowURL() {
if (data.domain !== undefined) {
return data.domain;
}
return targetLink;
return app.quit();
}
const APP_ICON = path.join(__dirname, '../resources', 'Icon');
@@ -62,97 +52,98 @@ const iconPath = () => {
return APP_ICON + (process.platform === 'win32' ? '.ico' : '.png');
};
function onClosed() {
// dereference the window
// for multiple windows store them in an array
mainWindow = null;
}
function updateDockBadge(title) {
if (title.indexOf('Zulip') === -1) {
return;
}
let messageCount = (/\(([0-9]+)\)/).exec(title);
messageCount = messageCount ? Number(messageCount[1]) : 0;
if (process.platform === 'darwin') {
app.setBadgeCount(messageCount);
}
}
function createMainWindow() {
// Load the previous state with fallback to defaults
const mainWindowState = windowStateKeeper({
defaultWidth: 1100,
defaultHeight: 720,
path: `${app.getPath('userData')}/config`
});
// Let's keep the window position global so that we can access it in other process
global.mainWindowState = mainWindowState;
const win = new electron.BrowserWindow({
// This settings needs to be saved in config
title: 'Zulip',
width: conf.get('width') || 1000,
height: conf.get('height') || 600,
icon: iconPath(),
minWidth: 600,
x: mainWindowState.x,
y: mainWindowState.y,
width: mainWindowState.width,
height: mainWindowState.height,
minWidth: 300,
minHeight: 400,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
plugins: true,
allowDisplayingInsecureContent: true,
nodeIntegration: false
nodeIntegration: true,
partition: 'persist:webviewsession'
},
show: false
});
win.on('focus', () => {
win.webContents.send('focus');
});
win.once('ready-to-show', () => {
if (ConfigUtil.getConfigItem('startMinimized')) {
win.minimize();
} else {
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();
}
}
});
win.loadURL(targetURL());
win.on('closed', onClosed);
win.setTitle('Zulip');
// Let's save browser window position
if (conf.get('x') || conf.get('y')) {
win.setPosition(conf.get('x'), conf.get('y'));
}
if (conf.get('maximize')) {
win.maximize();
}
// Handle sizing events so we can persist them.
win.on('maximize', () => {
conf.set('maximize', true);
win.on('enter-full-screen', () => {
win.webContents.send('enter-fullscreen');
});
win.on('unmaximize', () => {
conf.set('maximize', false);
win.on('leave-full-screen', () => {
win.webContents.send('leave-fullscreen');
});
win.on('resize', function () {
const size = this.getSize();
conf.set({
width: size[0],
height: size[1]
});
// To destroy tray icon when navigate to a new URL
win.webContents.on('will-navigate', e => {
if (e) {
win.webContents.send('destroytray');
}
});
// on osx it's 'moved'
win.on('move', function () {
const pos = this.getPosition();
conf.set({
x: pos[0],
y: pos[1]
});
});
// stop page to update it's title
win.on('page-title-updated', (e, title) => {
e.preventDefault();
updateDockBadge(title);
});
// 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;
}
// TODO - fix certificate errors
app.commandLine.appendSwitch('ignore-certificate-errors', 'true');
// Decrease load on GPU (experimental)
app.disableHardwareAcceleration();
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
// Temporary fix for Electron render colors differently
// More info here - https://github.com/electron/electron/issues/10732
app.commandLine.appendSwitch('force-color-profile', 'srgb');
// eslint-disable-next-line max-params
app.on('certificate-error', (event, webContents, url, error, certificate, callback) => {
event.preventDefault();
callback(true);
});
app.on('activate', () => {
@@ -162,54 +153,208 @@ app.on('activate', () => {
});
app.on('ready', () => {
electron.Menu.setApplicationMenu(appMenu);
appMenu.setMenu({
tabs: []
});
mainWindow = createMainWindow();
tray.create(mainWindow);
// Auto-hide menu bar on Windows + Linux
if (process.platform !== 'darwin') {
const shouldHideMenu = ConfigUtil.getConfigItem('autoHideMenubar') || false;
mainWindow.setAutoHideMenuBar(shouldHideMenu);
mainWindow.setMenuBarVisibility(!shouldHideMenu);
}
// Initialize sentry for main process
sentryInit();
const isSystemProxy = ConfigUtil.getConfigItem('useSystemProxy');
if (isSystemProxy) {
ProxyUtil.resolveSystemProxy(mainWindow);
}
const page = mainWindow.webContents;
// TODO - use global shortcut instead
electronLocalshortcut.register(mainWindow, 'CommandOrControl+R', () => {
mainWindow.reload();
});
electronLocalshortcut.register(mainWindow, 'CommandOrControl+[', () => {
if (page.canGoBack()) {
page.goBack();
}
});
electronLocalshortcut.register(mainWindow, 'CommandOrControl+]', () => {
if (page.canGoForward()) {
page.goForward();
}
});
page.on('dom-ready', () => {
page.insertCSS(fs.readFileSync(path.join(__dirname, 'preload.css'), 'utf8'));
mainWindow.show();
});
page.on('new-window', (event, url) => {
if (linkIsInternal(checkWindowURL(), url) && url.match(skipImages) === null) {
event.preventDefault();
return mainWindow.loadURL(url);
if (ConfigUtil.getConfigItem('startMinimized')) {
mainWindow.minimize();
} else {
mainWindow.show();
}
event.preventDefault();
electron.shell.openExternal(url);
});
page.once('did-frame-finish-load', () => {
// Initiate auto-updates on MacOS and Windows
if (ConfigUtil.getConfigItem('autoUpdate')) {
appUpdater();
}
});
// Temporarily remove this event
// electron.powerMonitor.on('resume', () => {
// mainWindow.reload();
// page.send('destroytray');
// });
ipcMain.on('focus-app', () => {
mainWindow.show();
});
ipcMain.on('quit-app', () => {
app.quit();
});
// Show pdf in a new BrowserWindow
ipcMain.on('pdf-view', (event, url) => {
// Paddings for pdfWindow so that it fits into the main browserWindow
const paddingWidth = 55;
const paddingHeight = 22;
// Get the config of main browserWindow
const mainWindowState = global.mainWindowState;
// Window to view the pdf file
const pdfWindow = new electron.BrowserWindow({
x: mainWindowState.x + paddingWidth,
y: mainWindowState.y + paddingHeight,
width: mainWindowState.width - paddingWidth,
height: mainWindowState.height - paddingHeight,
webPreferences: {
plugins: true,
partition: 'persist:webviewsession'
}
});
pdfWindow.loadURL(url);
// We don't want to have the menu bar in pdf window
pdfWindow.setMenu(null);
});
// Reload full app not just webview, useful in debugging
ipcMain.on('reload-full-app', () => {
mainWindow.reload();
page.send('destroytray');
});
ipcMain.on('clear-app-settings', () => {
global.mainWindowState.unmanage(mainWindow);
app.relaunch();
app.exit();
});
ipcMain.on('toggle-app', () => {
if (!mainWindow.isVisible() || mainWindow.isMinimized()) {
mainWindow.show();
} else {
mainWindow.hide();
}
});
ipcMain.on('toggle-badge-option', () => {
BadgeSettings.updateBadge(badgeCount, mainWindow);
});
ipcMain.on('toggle-menubar', (event, showMenubar) => {
mainWindow.setAutoHideMenuBar(showMenubar);
mainWindow.setMenuBarVisibility(!showMenubar);
});
ipcMain.on('update-badge', (event, messageCount) => {
badgeCount = messageCount;
BadgeSettings.updateBadge(badgeCount, mainWindow);
page.send('tray', messageCount);
});
ipcMain.on('update-taskbar-icon', (event, data, text) => {
BadgeSettings.updateTaskbarIcon(data, text, mainWindow);
});
ipcMain.on('forward-message', (event, listener, ...params) => {
page.send(listener, ...params);
});
ipcMain.on('update-menu', (event, props) => {
appMenu.setMenu(props);
});
ipcMain.on('toggleAutoLauncher', (event, AutoLaunchValue) => {
setAutoLaunch(AutoLaunchValue);
});
ipcMain.on('downloadFile', (event, url, downloadPath) => {
page.downloadURL(url);
page.session.once('will-download', (event, item) => {
const filePath = path.join(downloadPath, item.getFilename());
const getTimeStamp = () => {
const date = new Date();
return date.getTime();
};
const formatFile = filePath => {
const fileExtension = path.extname(filePath);
const baseName = path.basename(filePath, fileExtension);
return `${baseName}-${getTimeStamp()}${fileExtension}`;
};
// Update the name and path of the file if it already exists
const updatedFilePath = path.join(downloadPath, formatFile(filePath));
const setFilePath = fs.existsSync(filePath) ? updatedFilePath : filePath;
item.setSavePath(setFilePath);
item.on('updated', (event, state) => {
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.once('done', (event, state) => {
const getFileName = fs.existsSync(filePath) ? formatFile(filePath) : item.getFilename();
if (state === 'completed') {
page.send('downloadFileCompleted', item.getSavePath(), getFileName);
} else {
console.log('Download failed state: ', state);
page.send('downloadFileFailed');
}
// To stop item for listening to updated events of this file
item.removeAllListeners('updated');
});
});
});
ipcMain.on('realm-name-changed', (event, serverURL, realmName) => {
page.send('update-realm-name', serverURL, realmName);
});
ipcMain.on('realm-icon-changed', (event, serverURL, iconURL) => {
page.send('update-realm-icon', serverURL, iconURL);
});
});
ipc.on('new-domain', (e, domain) => {
// mainWindow.loadURL(domain);
if (!mainWindow) {
mainWindow = createMainWindow();
mainWindow.loadURL(domain);
} else if (mainWindow.isMinimized()) {
mainWindow.loadURL(domain);
mainWindow.show();
} else {
mainWindow.loadURL(domain);
}
targetLink = domain;
app.on('before-quit', () => {
isQuitting = true;
});
// Send crash reports
process.on('uncaughtException', err => {
console.error(err);
console.error(err.stack);
});

View File

@@ -1,15 +0,0 @@
const wurl = require('wurl');
// Check link if it's internal/external
function linkIsInternal(currentUrl, newUrl) {
const currentDomain = wurl('hostname', currentUrl);
const newDomain = wurl('hostname', newUrl);
return currentDomain === newDomain;
}
// We'll be needing this to open images in default browser
const skipImages = '.jpg|.gif|.png|.jpeg|.JPG|.PNG';
exports = module.exports = {
linkIsInternal, skipImages
};

52
app/main/linuxupdater.js Normal file
View File

@@ -0,0 +1,52 @@
const { app } = require('electron');
const { Notification } = require('electron');
const request = require('request');
const semver = require('semver');
const ConfigUtil = require('../renderer/js/utils/config-util');
const ProxyUtil = require('../renderer/js/utils/proxy-util');
const LinuxUpdateUtil = require('../renderer/js/utils/linux-update-util');
const Logger = require('../renderer/js/utils/logger-util');
const logger = new Logger({
file: 'linux-update-util.log',
timestamp: true
});
function linuxUpdateNotification() {
let url = 'https://api.github.com/repos/zulip/zulip-electron/releases';
url = ConfigUtil.getConfigItem('betaUpdate') ? url : url + '/latest';
const proxyEnabled = ConfigUtil.getConfigItem('useManualProxy') || ConfigUtil.getConfigItem('useSystemProxy');
const options = {
url,
headers: {'User-Agent': 'request'},
proxy: proxyEnabled ? ProxyUtil.getProxy(url) : ''
};
request(options, (error, response, body) => {
if (error) {
logger.error('Linux update error.');
logger.error(error);
return;
}
if (response.statusCode < 400) {
const data = JSON.parse(body);
const latestVersion = ConfigUtil.getConfigItem('betaUpdate') ? data[0].tag_name : data.tag_name;
if (semver.gt(latestVersion, app.getVersion())) {
const notified = LinuxUpdateUtil.getUpdateItem(latestVersion);
if (notified === null) {
new Notification({title: 'Zulip Update', body: 'A new version ' + latestVersion + ' is available. Please update using your package manager.'}).show();
LinuxUpdateUtil.setUpdateItem(latestVersion, true);
}
}
} else {
logger.log('Linux update response status: ', response.statusCode);
}
});
}
module.exports = {
linuxUpdateNotification
};

View File

@@ -1,331 +1,468 @@
'use strict';
const os = require('os');
const electron = require('electron');
const path = require('path');
const { app, shell, BrowserWindow, Menu, dialog } = require('electron');
const fs = require('fs-extra');
const AdmZip = require('adm-zip');
const { appUpdater } = require('./autoupdater');
const ConfigUtil = require(__dirname + '/../renderer/js/utils/config-util.js');
const DNDUtil = require(__dirname + '/../renderer/js/utils/dnd-util.js');
const Logger = require(__dirname + '/../renderer/js/utils/logger-util.js');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const shell = electron.shell;
const appName = app.getName();
const {addDomain, about} = require('./windowmanager');
const logger = new Logger({
file: 'errors.log',
timestamp: true
});
function sendAction(action) {
const win = BrowserWindow.getAllWindows()[0];
if (process.platform === 'darwin') {
win.restore();
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');
}
}
}];
}
win.webContents.send(action);
}
getToolsSubmenu() {
return [{
label: `Check for Updates`,
click() {
AppMenu.checkForUpdate();
}
},
{
label: `Release Notes`,
click() {
shell.openExternal(`https://github.com/zulip/zulip-electron/releases/tag/v${app.getVersion()}`);
}
}, {
type: 'separator'
}, {
label: 'Factory Reset',
accelerator: process.platform === 'darwin' ? 'Command+Shift+D' : 'Ctrl+Shift+D',
click() {
AppMenu.resetAppSettings();
}
}, {
label: 'Download App Logs',
click() {
const zip = new AdmZip();
let date = new Date();
date = date.toLocaleDateString().replace(/\//g, '-');
const viewSubmenu = [
{
label: 'Reload',
accelerator: 'CmdOrCtrl+R',
click(item, focusedWindow) {
if (focusedWindow) {
focusedWindow.reload();
// 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-${date}.zip`;
zip.writeZip(logFilePath);
// Open and select the log file
shell.showItemInFolder(logFilePath);
}
}
},
{
type: 'separator'
},
{
role: 'togglefullscreen'
},
{
label: 'Zoom In',
accelerator: 'CommandOrControl+=',
click(item, focusedWindow) {
if (focusedWindow) {
sendAction('zoomIn');
}, {
type: 'separator'
}, {
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: 'Zoom Out',
accelerator: 'CommandOrControl+-',
click(item, focusedWindow) {
if (focusedWindow) {
sendAction('zoomOut');
}, {
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');
}
}
}
},
{
label: 'Actual Size',
accelerator: 'CommandOrControl+0',
click(item, focusedWindow) {
if (focusedWindow) {
sendAction('zoomActualSize');
}
}
},
{
type: 'separator'
},
{
label: 'Toggle Developer Tools',
accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
click(item, focusedWindow) {
if (focusedWindow) {
focusedWindow.webContents.toggleDevTools();
}
}
}];
}
];
const helpSubmenu = [
{
label: `${appName} Website`,
click() {
shell.openExternal('https://zulip.org');
}
},
{
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)}`);
}
getViewSubmenu() {
return [{
label: 'Reload',
accelerator: 'CommandOrControl+R',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('reload-current-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: process.platform === 'darwin' ? 'Command+Plus' : 'Control+=',
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+Shift+S',
click(item, focusedWindow) {
if (focusedWindow) {
const newValue = !ConfigUtil.getConfigItem('showSidebar');
focusedWindow.webContents.send('toggle-sidebar', newValue);
ConfigUtil.setConfigItem('showSidebar', newValue);
}
}
}];
}
];
const darwinTpl = [
{
label: `${app.getName()}`,
submenu: [
getHelpSubmenu() {
return [
{
label: 'Zulip desktop',
click() {
about();
label: `${appName + ' Desktop'} v${app.getVersion()}`,
enabled: false
},
{
label: 'About Zulip',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('open-about');
}
}
},
{
label: `Help Center`,
click() {
shell.openExternal('https://zulipchat.com/help/');
}
}, {
label: 'Report an Issue',
click() {
// the goal is to notify the main.html BrowserWindow
// which may not be the focused window.
BrowserWindow.getAllWindows().forEach(window => {
window.webContents.send('open-feedback-modal');
});
}
}];
}
getWindowSubmenu(tabs, activeTabIndex) {
const initialSubmenu = [{
role: 'minimize'
}, {
role: 'close'
}];
if (tabs.length > 0) {
const ShortcutKey = process.platform === 'darwin' ? 'Cmd' : 'Ctrl';
initialSubmenu.push({
type: 'separator'
},
{
label: 'Change Zulip Server',
});
for (let i = 0; i < tabs.length; i++) {
// Do not add functional tab settings to list of windows in menu bar
if (tabs[i].props.role === 'function' && tabs[i].webview.props.name === 'Settings') {
continue;
}
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: 'checkbox'
});
}
}
return initialSubmenu;
}
getDarwinTpl(props) {
const { tabs, activeTabIndex, enableMenu } = props;
return [{
label: `${app.getName()}`,
submenu: [{
label: 'Desktop Settings',
accelerator: 'Cmd+,',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('open-settings');
}
}
}, {
label: 'Keyboard Shortcuts',
accelerator: 'Cmd+Shift+K',
enabled: enableMenu,
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('shortcut');
}
}
}, {
type: 'separator'
}, {
label: 'Toggle Do Not Disturb',
accelerator: 'Command+Shift+M',
click() {
addDomain();
const dndUtil = DNDUtil.toggle();
AppMenu.sendAction('toggle-dnd', dndUtil.dnd, dndUtil.newSettings);
}
},
{
label: 'Keyboard shortcuts',
accelerator: 'Cmd+K',
click(item, focusedWindow) {
if (focusedWindow) {
sendAction('shortcut');
}
}
},
{
type: 'separator'
},
{
}, {
label: 'Log Out',
accelerator: 'Cmd+L',
enabled: enableMenu,
click(item, focusedWindow) {
if (focusedWindow) {
sendAction('log-out');
AppMenu.sendAction('log-out');
}
}
},
{
}, {
type: 'separator'
},
{
}, {
role: 'services',
submenu: []
},
{
}, {
type: 'separator'
},
{
}, {
role: 'hide'
},
{
}, {
role: 'hideothers'
},
{
}, {
role: 'unhide'
},
{
}, {
type: 'separator'
},
{
}, {
role: 'quit'
}
]
},
{
label: 'Edit',
submenu: [
{
}]
}, {
label: 'Edit',
submenu: [{
role: 'undo'
},
{
}, {
role: 'redo'
},
{
}, {
type: 'separator'
},
{
}, {
role: 'cut'
},
{
}, {
role: 'copy'
},
{
}, {
role: 'paste'
},
{
}, {
role: 'pasteandmatchstyle'
},
{
role: 'delete'
},
{
}, {
role: 'selectall'
}
]
},
{
label: 'View',
submenu: viewSubmenu
},
{
role: 'window',
submenu: [
{
role: 'minimize'
},
{
role: 'close'
},
{
type: 'separator'
},
{
role: 'front'
}
]
},
{
role: 'help',
submenu: helpSubmenu
}]
}, {
label: 'View',
submenu: this.getViewSubmenu()
}, {
label: 'History',
submenu: this.getHistorySubmenu()
}, {
label: 'Window',
submenu: this.getWindowSubmenu(tabs, activeTabIndex)
}, {
label: 'Tools',
submenu: this.getToolsSubmenu()
}, {
role: 'help',
submenu: this.getHelpSubmenu()
}];
}
];
const otherTpl = [
{
label: 'File',
submenu: [
{
label: 'Zulip desktop',
click() {
about();
}
},
{
type: 'separator'
},
{
label: 'Change Zulip Server',
getOtherTpl(props) {
const { tabs, activeTabIndex, enableMenu } = props;
return [{
label: '&File',
submenu: [{
label: 'Desktop Settings',
accelerator: 'Ctrl+,',
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('open-settings');
}
}
}, {
type: 'separator'
}, {
label: 'Keyboard Shortcuts',
accelerator: 'Ctrl+Shift+K',
enabled: enableMenu,
click(item, focusedWindow) {
if (focusedWindow) {
AppMenu.sendAction('shortcut');
}
}
}, {
type: 'separator'
}, {
label: 'Toggle Do Not Disturb',
accelerator: 'Ctrl+Shift+M',
click() {
addDomain();
const dndUtil = DNDUtil.toggle();
AppMenu.sendAction('toggle-dnd', dndUtil.dnd, dndUtil.newSettings);
}
},
{
type: 'separator'
},
{
label: 'Keyboard shortcuts',
accelerator: 'Ctrl+K',
click(item, focusedWindow) {
if (focusedWindow) {
sendAction('shortcut');
}
}
},
{
type: 'separator'
},
{
}, {
label: 'Log Out',
accelerator: 'Ctrl+L',
enabled: enableMenu,
click(item, focusedWindow) {
if (focusedWindow) {
sendAction('log-out');
AppMenu.sendAction('log-out');
}
}
},
{
}, {
type: 'separator'
},
{
role: 'quit'
}
]
},
{
label: 'Edit',
submenu: [
{
}, {
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: viewSubmenu
},
{
role: 'help',
submenu: helpSubmenu
}]
}, {
label: '&View',
submenu: this.getViewSubmenu()
}, {
label: '&History',
submenu: this.getHistorySubmenu()
}, {
label: '&Window',
submenu: this.getWindowSubmenu(tabs, activeTabIndex)
}, {
label: '&Tools',
submenu: this.getToolsSubmenu()
}, {
label: '&Help',
role: 'help',
submenu: this.getHelpSubmenu()
}];
}
];
const tpl = process.platform === 'darwin' ? darwinTpl : otherTpl;
static sendAction(action, ...params) {
const win = BrowserWindow.getAllWindows()[0];
module.exports = electron.Menu.buildFromTemplate(tpl);
if (process.platform === 'darwin') {
win.restore();
}
win.webContents.send(action, ...params);
}
static checkForUpdate() {
appUpdater(true);
}
static resetAppSettings() {
const resetAppSettingsMessage = 'By proceeding you will be removing all connected organizations and preferences from Zulip.';
// We save App's settings/configurations in following files
const settingFiles = ['config/window-state.json', 'config/domain.json', 'config/settings.json', 'config/certificates.json'];
dialog.showMessageBox({
type: 'warning',
buttons: ['YES', 'NO'],
defaultId: 0,
message: 'Are you sure?',
detail: resetAppSettingsMessage
}, response => {
if (response === 0) {
settingFiles.forEach(settingFileName => {
const getSettingFilesPath = path.join(app.getPath('appData'), appName, settingFileName);
fs.access(getSettingFilesPath, error => {
if (error) {
logger.error('Error while resetting app settings.');
logger.error(error);
} else {
fs.unlink(getSettingFilesPath, () => {
AppMenu.sendAction('clear-app-data');
});
}
});
});
}
});
}
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();

View File

@@ -1 +0,0 @@
/* We'll be overriding default styling so that app look more native * /

View File

@@ -1,52 +0,0 @@
'use strict';
const ipcRenderer = require('electron').ipcRenderer;
// Handle zooming functionality
const zoomIn = () => {
webFrame.setZoomFactor(webFrame.getZoomFactor() + 0.1);
};
const zoomOut = () => {
webFrame.setZoomFactor(webFrame.getZoomFactor() - 0.1);
};
const zoomActualSize = () => {
webFrame.setZoomFactor(1);
};
// Get zooming actions from main process
ipcRenderer.on('zoomIn', () => {
zoomIn();
});
ipcRenderer.on('zoomOut', () => {
zoomOut();
});
ipcRenderer.on('zoomActualSize', () => {
zoomActualSize();
});
ipcRenderer.on('log-out', () => {
// 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();
});
ipcRenderer.on('shortcut', () => {
// create the menu for the below
document.querySelector('.dropdown-toggle').click();
const nodes = document.querySelectorAll('.dropdown-menu li:nth-child(4) a');
nodes[nodes.length - 1].click();
});
require('./domain');
// To prevent failing this script on linux we need to load it after the document loaded
document.addEventListener('DOMContentLoaded', function () {
require('./spellchecker');
});

View File

@@ -1,14 +0,0 @@
const {SpellCheckHandler, ContextMenuListener, ContextMenuBuilder} = require('electron-spellchecker');
// Implement spellcheck using electron api
window.spellCheckHandler = new SpellCheckHandler();
window.spellCheckHandler.attachToInput();
// Start off as US English
window.spellCheckHandler.switchLanguage('en-US');
let contextMenuBuilder = new ContextMenuBuilder(window.spellCheckHandler);
let contextMenuListener = new ContextMenuListener((info) => {
contextMenuBuilder.showPopupMenu(info);
});

34
app/main/startup.js Normal file
View File

@@ -0,0 +1,34 @@
'use strict';
const { app } = require('electron');
const AutoLaunch = require('auto-launch');
const isDev = require('electron-is-dev');
const ConfigUtil = require('./../renderer/js/utils/config-util.js');
const setAutoLaunch = AutoLaunchValue => {
// Don't run this in development
if (isDev) {
return;
}
// On Mac, work around a bug in auto-launch where it opens a Terminal window
// See https://github.com/Teamwork/node-auto-launch/issues/28#issuecomment-222194437
const appPath = process.platform === 'darwin' ? app.getPath('exe').replace(/\.app\/Content.*/, '.app') : undefined; // Use the default
const ZulipAutoLauncher = new AutoLaunch({
name: 'Zulip',
path: appPath,
isHidden: false
});
const autoLaunchOption = ConfigUtil.getConfigItem('startAtLogin', AutoLaunchValue);
if (autoLaunchOption) {
ZulipAutoLauncher.enable();
} else {
ZulipAutoLauncher.disable();
}
};
module.exports = {
setAutoLaunch
};

View File

@@ -1,58 +0,0 @@
'use strict';
const path = require('path');
const electron = require('electron');
const app = require('electron').app;
const {addDomain, about} = require('./windowmanager');
let tray = null;
const APP_ICON = path.join(__dirname, '../resources', 'Icon');
const iconPath = () => {
return APP_ICON + (process.platform === 'win32' ? '.ico' : '.png');
};
exports.create = () => {
const contextMenu = electron.Menu.buildFromTemplate([
{
label: 'About',
click() {
about();
}
},
{
type: 'separator'
},
{
label: 'Change Zulip server',
click() {
addDomain();
}
},
{
type: 'separator'
},
{
label: 'Reload',
click(item, focusedWindow) {
if (focusedWindow) {
focusedWindow.reload();
}
}
},
{
type: 'separator'
},
{
label: 'Quit',
click() {
app.quit();
}
}
]);
tray = new electron.Tray(iconPath());
tray.setToolTip(`${app.getName()}`);
tray.setContextMenu(contextMenu);
};

View File

@@ -1,69 +0,0 @@
'use strict';
const path = require('path');
const electron = require('electron');
let domainWindow;
let aboutWindow;
function onClosed() {
// dereference the window
domainWindow = null;
aboutWindow = null;
}
// Change Zulip server Window
function createdomainWindow() {
const domainwin = new electron.BrowserWindow({
frame: false,
height: 300,
resizable: false,
width: 400
});
const domainURL = 'file://' + path.join(__dirname, '../renderer', 'pref.html');
domainwin.loadURL(domainURL);
domainwin.on('closed', onClosed);
return domainwin;
}
// Call this window onClick addDomain in tray
function addDomain() {
domainWindow = createdomainWindow();
domainWindow.show();
}
// About window
function createAboutWindow() {
const aboutwin = new electron.BrowserWindow({
width: 500,
height: 500,
title: 'About Zulip Desktop',
show: false,
center: true,
fullscreen: false,
fullscreenable: false,
resizable: false
});
const aboutURL = 'file://' + path.join(__dirname, '../renderer', 'about.html');
aboutwin.loadURL(aboutURL);
aboutwin.on('closed', onClosed);
// stop page to update it's title
aboutwin.on('page-title-updated', e => {
e.preventDefault();
});
aboutwin.on('closed', onClosed);
return aboutwin;
}
// Call this onClick About in tray
function about() {
aboutWindow = createAboutWindow();
aboutWindow.show();
}
exports = module.exports = {
addDomain, about
};

1657
app/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,13 @@
{
"name": "zulip",
"productName": "Zulip",
"version": "0.5.1",
"version": "2.3.82",
"description": "Zulip Desktop App",
"license": "Apache-2.0",
"email":"<svnitakash@gmail.com>",
"copyright": "©2016 Kandra Labs, Inc.",
"copyright": "Kandra Labs, Inc.",
"author": {
"name": "Akash Nimare",
"email": "svnitakash@gmail.com"
"name": "Kandra Labs, Inc.",
"email": "support@zulipchat.com"
},
"repository": {
"type": "git",
@@ -27,15 +26,23 @@
"InstantMessaging"
],
"dependencies": {
"configstore": "^2.0.0",
"dialogs": "1.1.14",
"electron-context-menu": "0.4.0",
"electron-debug": "^1.0.0",
"electron-dl": "^0.2.0",
"electron-localshortcut": "^0.6.1",
"node-json-db": "^0.7.2",
"request": "^2.74.0",
"electron-spellchecker": "^0.5.12",
"wurl": "^2.1.0"
"@electron-elements/send-feedback": "1.0.8",
"@sentry/electron": "0.12.1",
"adm-zip": "0.4.11",
"auto-launch": "5.0.5",
"electron-is-dev": "0.3.0",
"electron-log": "2.2.14",
"electron-spellchecker": "1.1.2",
"electron-updater": "3.1.2",
"electron-window-state": "4.1.1",
"escape-html": "1.0.3",
"is-online": "7.0.0",
"node-json-db": "0.9.1",
"request": "2.85.0",
"semver": "5.4.1",
"wurl": "2.5.0"
},
"optionalDependencies": {
"node-mac-notifier": "0.1.0"
}
}

View File

@@ -1,31 +1,46 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="css/about.css">
</head>
<body>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="css/about.css">
</head>
<body>
<div class="about">
<center><img src="../resources/zulip.png"></center>
<center><p class="detail" id="version"> Version : ?.?.? </p>
<center><p class="detail"> License : Apache </p>
<center><p class="detail"> Maintainer : Zulip </p>
<p class="left"><a class="bug" onclick="linkInBrowser()" href="#">Found bug?</a></p>
<img class="logo" src="../resources/zulip.png" />
<p class="detail" id="version">v?.?.?</p>
<div class="maintenance-info">
<p class="detail maintainer">
Maintained by
<a onclick="linkInBrowser('website')">Zulip</a>
</p>
<p class="detail license">
Available under the
<a onclick="linkInBrowser('license')">Apache 2.0 License</a>
</p>
</div>
</div>
<script>
const app = require('electron').remote.app;
const version_tag = document.getElementById('version');
version_tag.innerHTML = ' Version : ' + app.getVersion() + ' ';
const { app } = require('electron').remote;
const { shell } = require('electron');
const version_tag = document.querySelector('#version');
version_tag.innerHTML = 'v' + 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);
}
function linkInBrowser(type) {
let url;
switch (type) {
case 'website':
url = "https://zulipchat.com";
break;
case 'license':
url = "https://github.com/zulip/zulip-electron/blob/master/LICENSE";
break;
}
shell.openExternal(url);
}
</script>
</body>
<script>require('./js/shared/preventdrag.js')</script>
</body>
</html>

View File

@@ -1,15 +1,24 @@
body {
background: #6BB6C7;
background: #fafafa;
font-family: menu, "Helvetica Neue", sans-serif;
-webkit-font-smoothing: subpixel-antialiased;
}
.logo {
display: block;
margin: -40px auto;
}
#version {
color: #444343;
font-size: 1.3em;
padding-top: 40px;
}
.about {
margin-top: 50px;
}
.left {
position: absolute;
top:89%;
left:76%;
margin: 25vh auto;
height: 25vh;
text-align: center;
}
.about p {
@@ -18,10 +27,40 @@ body {
}
.about img {
width:160px;
width: 150px;
}
.detail {
text-align: left;
margin-left: 35%;
}
text-align: center;
}
.detail.maintainer {
font-size: 1.2em;
font-weight: 500;
}
.detail.license {
font-size: 0.8em;
}
.maintenance-info {
cursor: pointer;
position: absolute;
width: 100%;
left: 0px;
color: #444;
}
.maintenance-info p {
margin: 0;
font-size: 1em;
width: 100%;
}
p.detail a {
color: #355f4c;
}
p.detail a:hover {
text-decoration: underline;
}

View File

@@ -1,375 +1,476 @@
@charset "UTF-8";
header,
section {
display: block
}
html {
font-size: 100%;
overflow-y: scroll;
-webkit-text-size-adjust: 100%;
}
html,
button,
input {
font-family: "Helvetica Neue", Arial, sans-serif
}
/*******************
* General rules *
*******************/
html,
body {
height: 100%;
margin: 0
}
img {
border: 0;
-ms-interpolation-mode: bicubic
}
img {
vertical-align: middle
}
form {
margin: 0
}
fieldset {
border: 0;
margin: 0;
padding: 0
cursor: default;
user-select: none;
}
button,
input {
font-size: 100%;
margin: 0;
vertical-align: baseline;
box-sizing: content-box;
-webkit-box-sizing: content-box
#content {
display: flex;
height: 100%;
}
button,
input {
line-height: normal
.toggle-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;
transition: all 0.5s ease;
z-index: 2;
}
button {
cursor: pointer;
-webkit-appearance: button;
.toggle-sidebar div {
transition: all 0.5s ease-out;
}
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0
.sidebar-hide {
width: 0;
transition: all 0.8s ease;
}
hr {
display: none
.sidebar-hide div {
transform: translateX(-100%);
transition: all 0.6s ease-out;
}
img {
max-width: 100%
#view-controls-container {
height: calc(100% - 208px);
overflow-y: hidden;
}
h1 {
color: #111;
line-height: 1em;
#view-controls-container:hover {
overflow-y: overlay;
}
#view-controls-container::-webkit-scrollbar {
width: 4px;
}
#view-controls-container::-webkit-scrollbar-track {
box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
}
#view-controls-container::-webkit-scrollbar-thumb {
background-color: darkgrey;
outline: 1px solid slategrey;
}
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
font-family: "Helvetica Neue", Arial, sans-serif;
text-rendering: optimizelegibility;
-webkit-text-stroke: none
}
h1 {
margin: 0 0 35px
src: local('Material Icons'), local('MaterialIcons-Regular'), url(../fonts/MaterialIcons-Regular.ttf) format('truetype');
}
body.container-layout header #logo {
/*******************
* Left Sidebar *
*******************/
#tabs-container {
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;
}
#actions-container {
display: flex;
flex-direction: column;
position: fixed;
bottom: 0;
}
.action-button {
display: flex;
flex-direction: column;
padding: 12px;
}
.action-button:hover {
cursor: pointer;
}
.action-button i {
color: #6c8592;
font-size: 28px;
}
.action-button:hover i {
color: #98a9b3;
}
.action-button.disable {
opacity: 0.6;
}
.action-button.disable:hover {
cursor: not-allowed;
}
.action-button.disable:hover i {
color: #6c8592;
}
.action-button.active {
/* background-color: rgba(255, 255, 255, 0.25); */
background-color: #efefef;
opacity: 0.9;
padding-right: 14px;
}
.action-button.active i {
color: #1c262b;
}
.tab:first-child {
margin-top: 9px;
}
.tab {
position: relative;
display: inline-block;
vertical-align: top
margin: 2px 0;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
body.container-layout header #logo {
width: 50px;
height: 50px;
background-position: -790px 0
}
button[type=submit] {
text-decoration: none;
display: inline-block;
.tab .server-icons {
width: 35px;
vertical-align: top;
-webkit-box-sizing: border-box;
box-sizing: border-box;
-webkit-border-radius: 4px;
border-radius: 4px;
}
.tab .server-tab {
width: 100%;
height: 35px;
position: relative;
font-family: "Helvetica Neue", Arial, sans-serif;
font-size-adjust: auto;
vertical-align: bottom;
background-color: #e6eaef;
border: 2px solid #e6eaef;
color: #96a0ac;
font-size: 20px;
line-height: 26px;
padding: 6px 17px
margin-top: 5px;
z-index: 11;
line-height: 31px;
color: #eee;
text-align: center;
overflow: hidden;
opacity: 0.6;
padding: 6px 0;
}
.desktop button[type=submit] {
-webkit-transition: all .2s ease-in-out;
transition: all .2s ease-in-out
}
.desktop button[type=submit]:hover {
background-color: #eff2f5;
border: 2px solid #eff2f5;
color: #96a0ac
}
button[type=submit]:focus {
outline: 0
}
button[type=submit].btn-primary {
background-color: #20b36c;
border: 2px solid #20b36c;
color: #fff
}
.desktop button[type=submit].btn-primary:hover {
background-color: #39ca83;
border: 2px solid #39ca83;
color: #fff
}
button[type=submit].btn-primary:focus {
outline: 0
}
button[type=submit].btn-large {
font-family: "Helvetica Neue", Arial, sans-serif;
font-size: 20px;
line-height: 26px;
padding: 12px 30px
}
input[type=text] {
font-family: "Helvetica Neue", Arial, sans-serif;
font-weight: 200;
display: inline-block;
vertical-align: top;
-webkit-border-radius: 4px;
.server-tab .alt-icon {
font-family: Verdana;
font-weight: 600;
font-size: 22px;
border: 2px solid #222c31;
margin-left: 17%;
width: 35px;
border-radius: 4px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
background-color: #fff;
border: 1px solid #cad0d7;
color: #000;
font-size: 18px;
line-height: 26px;
height: 42px;
padding: 10px 10px
}
.desktop input[type=text]:hover {
background-color: #fff;
border-color: #bbc3cc;
color: #111
.tab .server-tab:hover {
opacity: 0.8;
}
input[type=text]:focus {
background-color: #fff;
border-color: #20b36c!important;
color: #000;
outline: 0
.tab.functional-tab {
height: 46px;
padding: 0;
}
input[type=text]::-webkit-input-placeholder {
color: #8e959e
.tab.functional-tab.active .server-tab {
padding: 2px 0;
height: 40px;
background-color: rgba(255, 255, 255, 0.25);
}
input[type=text]::-moz-placeholder {
color: #8e959e
.tab.functional-tab .server-tab i {
font-size: 28px;
line-height: 36px;
}
input[type=text]:-ms-input-placeholder {
color: #8e959e
.tab.active .server-tab {
opacity: 1;
background-color: #648478;
}
.form-large input[type=text] {
font-size: 18px;
line-height: 26px;
height: 54px;
padding: 12px 15px
}
.control-group {
margin-top: 40px
}
h1 {
white-space: normal;
word-break: break-all;
word-break: break-word;
-webkit-hyphens: auto;
-moz-hyphens: auto;
hyphens: auto
}
.section-main {
position: relative
}
body {
color: #111;
font-size: 18px;
line-height: 24px;
font-family: "Helvetica Neue", Arial, sans-serif;
background: #edf1f3;
-webkit-box-sizing: border-box;
box-sizing: border-box
}
.desktop body {
padding-top: 116px
}
body.container-layout {
padding: 0!important;
background-color: #E6EAEF
}
header {
z-index: 800;
border-bottom: 1px solid #dae0e7;
background: rgba(255, 255, 255, .97);
-webkit-box-sizing: border-box;
box-sizing: border-box;
position: relative;
top: 0;
left: 0;
width: 100%
}
header .container {
position: relative
}
.desktop header {
position: fixed
}
header .container {
position: relative;
height: 75px
}
.container-layout header {
position: relative;
border: 0;
background: 0 0;
padding: 30px 0
}
.container-layout header .container {
height: auto
}
header #logo {
.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;
text-indent: -9999px
right: 0;
}
header #logo {
position: absolute!important;
top: 50%;
left: 10px;
-webkit-transform: translate(0, -50%);
transform: translate(0, -50%)
.tab .server-tab-badge {
display: none;
}
body.container-layout header #logo {
position: relative!important;
top: 0;
left: 0;
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
margin: 0 auto;
display: block
.tab .server-tab-badge.close-button {
width: 16px;
padding: 0;
}
.content {
padding-bottom: 40px;
overflow: hidden
.tab .server-tab-badge.close-button i {
font-size: 13px;
line-height: 17px;
}
.container-layout .content {
max-width: 660px;
margin: 0 auto
#add-tab {
display: flex;
align-items: center;
flex-direction: column;
}
.content .server {
position: relative;
margin: 0 10px
.tab .server-tab-shortcut {
color: #648478;
font-size: 12px;
text-align: center;
font-family: sans-serif;
margin-bottom: 5px;
}
.content .server .container {
background: #fff;
-webkit-box-sizing: border-box;
box-sizing: border-box;
padding: 50px 0 0;
-webkit-border-radius: 4px;
/*******************
* Webview Area *
*******************/
#webviews-container {
display: flex;
height: 100%;
width: 100%;
}
/*Pseudo element for loading indicator*/
#webviews-container::before {
content: "";
position: absolute;
z-index: 1;
background: #fff url(../img/ic_loading.gif) no-repeat;
background-size: 60px 60px;
background-position: center;
width: 100%;
height: 100%;
}
/*When the active webview is loaded*/
#webviews-container.loaded::before {
opacity: 0;
z-index: -1;
visibility: hidden;
}
webview {
/* transition: opacity 0.3s ease-in; */
flex-grow: 1;
position: absolute;
width: 100%;
height: 100%;
flex-grow: 1;
display: flex;
flex-direction: column;
}
webview.onload {
transition: opacity 1s cubic-bezier(0.95, 0.05, 0.795, 0.035);
}
webview.active {
opacity: 1;
z-index: 1;
visibility: visible;
}
webview.disabled {
opacity: 0;
}
webview.focus {
outline: 0px solid transparent;
}
/* Tooltip styling */
#dnd-tooltip,
#back-tooltip,
#reload-tooltip,
#setting-tooltip {
font-family: sans-serif;
background: #222c31;
margin-left: 48px;
padding: 6px 8px;
position: absolute;
margin-top: 0px;
z-index: 1000;
color: white;
border-radius: 4px;
text-align: center;
width: 55px;
font-size: 14px;
}
#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 #222c31;
position: absolute;
top: 7px;
right: 68px;
}
#add-server-tooltip,
.server-tooltip {
font-family: 'arial';
background: #222c31;
left: 56px;
padding: 10px 20px;
position: fixed;
margin-top: 11px;
z-index: 5000 !important;
color: #fff;
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 #222c31;
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;
}
#collapse-button i {
color: #efefef;
}
#main-container {
display: flex;
height: 100%;
width: 100%;
position: relative;
z-index: 10;
max-width: 580px
flex-grow: 1;
flex-basis: 0px;
}
.content .server h1 {
.hidden {
display: none !important;
}
/* Full screen Popup container */
.popup .popuptext {
visibility: hidden;
background-color: #555;
color: #fff;
text-align: center;
padding: 0 10%
border-radius: 6px;
padding: 9px 0;
position: absolute;
z-index: 1000;
font-family: arial;
width: 240px;
top: 15px;
height: 20px;
left: 43%;
}
.content .server h1 {
font-size: 2.4em;
line-height: 1.2em;
margin-bottom: 10px
.popup .show {
visibility: visible;
animation: cssAnimation 0s ease-in 5s forwards;
animation-fill-mode: forwards;
}
.content .server fieldset {
padding: 25px 10% 80px 39px;
position: relative
}
.content .server fieldset .control-group .control-field input {
width: 100%
}
.content .server button {
width: 100%
}
@media screen and (min-width: 749px) {
input[type=text] {
width: 60%
@keyframes cssAnimation {
from {
opacity: 0;
}
to {
width: 0;
height: 0;
overflow: hidden;
opacity: 1;
}
}
@media screen and (min-width: 1071px) {
h1 {
font-size: 3.2em;
line-height: 1.3em
}
send-feedback {
width: 60%;
height: 85%;
}
.container {
width: 1070px;
margin-left: auto;
margin-right: auto;
padding-left: 10px;
padding-right: 10px;
#feedback-modal {
display: none;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(68, 67, 67, 0.81);
align-items: center;
justify-content: center;
z-index: 2;
transition: all 1s ease-out;
}
.container:before,
.container:after {
content: "";
display: table
}
.container:after {
clear: both
}
.responsive .container {
max-width: 1070px;
width: auto;
margin-left: auto;
margin-right: auto;
padding-left: 10px;
padding-right: 10px;
}
.responsive .container:before,
.responsive .container:after {
content: "";
display: table
}
.responsive .container:after {
clear: both
}
@media screen and (max-width: 480px) {
.responsive h1 {
font-size: 1.7em;
line-height: 1.3em
}
}
@media screen and (min-width: 481px) and (max-width: 640px) {
.responsive h1 {
font-size: 1.9em;
line-height: 1.3em
}
}
@media screen and (min-width: 641px) and (max-width: 748px) {
.responsive h1 {
font-size: 2.2em;
line-height: 1.3em
}
}
@media screen and (max-width: 748px) {
.responsive input[type=text] {
width: 100%
}
}
@media screen and (min-width: 749px) and (max-width: 1070px) {
.responsive h1 {
font-size: 2.6em;
line-height: 1.3em
}
}
#server-status {
text-align: center;
color: #c71212;
#feedback-modal.show {
display: flex;
}

View File

@@ -0,0 +1,42 @@
html, body {
margin: 0;
cursor: default;
font-size: 14px;
color: #333;
background: #fff;
user-select:none;
}
#content {
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;
}
#description {
font-size: 16px;
}
#reconnect {
font-size: 16px;
background: #009688;
color: #fff;
width: 84px;
height: 32px;
border-radius: 5px;
line-height: 32px;
margin: 20px auto 0;
cursor: pointer;
}
#reconnect:hover {
opacity: 0.8;
}

View File

@@ -1,83 +0,0 @@
body{
background-color: #6BB6C7;
}
.form {
position: absolute;
top: 35%;
width: 300px;
left: 9%;
}
.close {
background: transparent url('../img/close.png') no-repeat 4px 4px;
background-size: 24px 24px;
cursor: pointer;
display: inline-block;
height: 32px;
position: absolute;
right: 6px;
text-indent: -10000px;
top: 6px;
width: 32px;
z-index: 1;
-webkit-app-region: no-drag;
}
input[type="text"] {
display: block;
margin: 0;
width: 100%;
font-family: sans-serif;
font-size: 18px;
appearance: none;
box-shadow: none;
border-radius: none;
color: #646464;
}
input[type="text"]:focus {
outline: none;
}
.form input[type="text"] {
padding: 10px;
border: solid 1px #dcdcdc;
transition: box-shadow 0.3s, border 0.3s;
}
.form input[type="text"]:focus,
.form input[type="text"].focus {
border: solid 1px #707070;
box-shadow: 0 0 5px 1px #969696;
}
button {
border: none;
color: #fff;
padding: 12px 32px;
text-align: center;
text-decoration: none;
margin-left: 107px;
margin-top: 24px;
display: inline-block;
font-size: 16px;
background: #137b86;
}
button:focus {
outline: 0;
}
#urladded {
font-size: 20px;
position: absolute;
font-family: 'opensans';
top: -61%;
left: 25%;
text-align: center;
}
#pic {
width: 20px;
left: 36%;
margin-left: 4px;
top: 63%;
display: none;
position: absolute;
}

View File

@@ -0,0 +1,671 @@
html,
body {
height: 100%;
margin: 0;
cursor: default;
user-select: none;
font-family: menu, "Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 14px;
background: #efefef;
letter-spacing: -.08px;
line-height: 18px;
color: #8b8e8f;
}
kbd {
display: inline-block;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 15px;
font-family: Courier New, Courier, monospace;
font-weight: bold;
white-space: nowrap;
background-color: #f7f7f7;
color: #333;
margin: 0 0.1em;
padding: 0.3em 0.8em;
text-shadow: 0 1px 0 #fff;
line-height: 1.4;
}
table,
th,
td {
border-collapse: collapse;
color: #383430;
}
table {
width: 100%;
margin-top: 18px;
margin-bottom: 18px;
}
table tr:nth-child(even) {
background-color: #fafafa;
}
table tr:nth-child(odd) {
background-color: #fff;
}
td {
padding: 5px;
}
td:nth-child(odd) {
text-align: right;
width: 50%;
}
@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');
}
.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;
}
#content {
display: flex;
height: 100%;
font-family: 'Montserrat';
}
#sidebar {
width: 150px;
min-width: 100px;
padding: 30px 30px 30px 35px;
display: flex;
flex-direction: column;
font-size: 16px;
background: #f2f2f2;
}
#nav-container {
padding: 20px 0;
}
.nav {
padding: 7px 0;
color: #999;
cursor: pointer;
}
.nav.active {
color: #4ebfac;
cursor: default;
position: relative;
}
.nav.active::before {
background: #464e5a;
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 */
#nav-AddServer {
display: none;
}
#settings-header {
font-size: 22px;
color: #222c31;
font-weight: bold;
text-transform: uppercase;
}
#settings-container {
width: 100%;
display: flex;
padding: 30px;
overflow-y: scroll;
}
#new-server-container {
padding-left: 42px;
padding-top: 25px;
margin-right: 16px;
}
.title {
font-weight: 500;
color: #222c31;
}
.page-title {
color: #222c31;
font-size: 15px;
font-weight: bold;
padding: 4px 0 6px 0;
}
.add-server-info-row {
display: flex;
margin: 8px 0 0 0;
}
.add-server-info-right {
flex-grow: 1;
margin-top: 10px;
}
.sub-title {
padding: 4px 0 6px 0;
font-weight: bold;
color: #616161;
}
img.server-info-icon {
width: 36px;
height: 36px;
padding: 4px 4px 4px 10px;
cursor: pointer;
vertical-align: middle;
}
.server-info-left {
margin: 4px 20px 0 0;
min-width: 40%;
}
.server-info-right {
margin-top: 4px;
width: 55%;
display: flex;
align-items: baseline;
justify-content: space-between;
flex-grow: 1;
}
.server-info-row {
display: inline-block;
margin: 5px 0 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;
}
.server-url-info {
margin-right: 10px;
}
.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: #ededed 2px solid;
outline-width: 0;
background: transparent;
max-width: 450px;
}
.setting-input-value:focus {
border: #4EBFAC 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;
}
.action {
display: flex;
align-items: center;
padding: 0 10px;
margin-right: 10px;
}
.action i {
margin-right: 5px;
font-size: 18px;
line-height: 26px;
}
.settings-pane {
flex-grow: 1;
}
.action:hover {
cursor: pointer;
}
.action.disabled:hover {
cursor: default;
}
.action.disabled {
color: #999;
}
.settings-card {
display: flex;
flex-wrap: wrap;
padding: 12px 30px;
margin: 10px 0 20px 0;
background: #fff;
width: 80%;
transition: all 0.2s;
}
.settings-card:hover {
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 0px 0px rgba(0, 0, 0, 0.12);
}
.hidden {
display: none;
margin: 5px !important;
}
.red {
color: #ef5350;
padding: 8px;
border: rgba(239, 83, 80, 0.5) solid 1px;
border-radius: 4px;
border-color: #fff;
}
.red:hover {
background-color: rgba(230, 52, 49, 0.5);
color: #fff;
}
.green {
color: #fff;
background: #4EBFAC;
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: #3c9f8d;
color: #fff;
}
.w-150 {
width: 150px;
}
.grey {
color: #9E9E9E;
background: #FAFAFA;
border: 1px solid #9E9E9E;
}
.setting-row {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
margin: 6px;
}
.code {
font-family: Courier New, Courier, monospace;
}
i.open-tab-button {
padding-left: 5px;
font-size: 19px;
cursor: pointer;
}
.css-delete-action {
margin-bottom: 10px;
}
.selected-css-path,
.download-folder-path {
background: #eeeeee;
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);
}
#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;
}
input.toggle-round+label {
padding: 2px;
width: 50px;
height: 25px;
background-color: #dddddd;
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: #f1f1f1;
border-radius: 25px;
top: 0;
right: 0px;
left: 0px;
bottom: 0px;
}
input.toggle-round+label:after {
width: 25px;
height: 25px;
background-color: #fff;
border-radius: 100%;
}
input.toggle-round:checked+label:before {
background-color: #4EBFAC;
top: 0;
right: 0px;
left: 0px;
bottom: 0px;
}
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, #003b52, #45b59b);
overflow: auto;
}
/* Modal Content */
.modal-container {
background-color: #f4f7f8;
margin: auto;
padding: 57px;
border: #dae1e3 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: #7d878a;
}
.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: #fff;
background: #4EBFAC;
border-color: none;
border: none;
width: 98%;
height: 46px;
border-radius: 4px;
cursor: pointer;
}
.server-center button:hover {
background: #329588;
}
.server-center button:focus {
background: #329588;
}
.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%;
}
#add-certificate-button {
margin: 10px 10px 10px 37px;
}
.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;
}
/* responsive grid */
@media (max-width: 650px) {
.selected-css-path,
.download-folder-path {
margin-right: 15px;
}
#css-delete-action {
margin-left: 10px;
}
#css-delete-action span {
display: none;
}
}
@media (max-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%;
}
#new-server-container {
padding-left: 0px;
}
}
@media (max-width: 600px) {
.divider {
margin-left: 4%;
}
.divider hr {
margin-left: 2px;
margin-right: 2px;
width: 40%;
}
}
@media (max-width: 900px) {
.settings-card {
flex-direction: column;
align-items: center;
}
.server-info-right {
flex-direction: column;
align-items: center;
}
.action {
margin-top: 10px;
}
}

View File

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 803 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
app/renderer/img/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -1,42 +0,0 @@
<!DOCTYPE html>
<html lang="en" class="responsive desktop">
<!--<![endif]-->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width">
<title>Login - Zulip</title>
<link rel="stylesheet" href="css/main.css" type="text/css" media="screen">
</head>
<body class="container-layout">
<div class="section-main">
<header>
<div class="container">
<img src="../resources/zulip.png" id="logo"/>
</div>
</header>
<hr>
<section class="content">
<section class="server">
<div class="container">
<h1>Login to Zulip Server</h1>
<form id="frm-signInForm" class="form-large" onsubmit="addDomain(); return false">
<fieldset>
<div class="control-group control-required">
<div class="control-field">
<input type="text" id="url" autofocus="autofocus" spellcheck="false" placeholder="zulip.tabbott.net">
</div>
</div>
<div class="control-group">
<div class="control-submit">
<button type="submit" id="main" class="btn-primary btn-large" value="Submit" onclick="addDomain();">Connect</button>
</div>
</div>
<p id="server-status"><p>
</fieldset>
</form>
</div>
</section>
</section>
</div>
</body>
</html>

View File

@@ -0,0 +1,11 @@
'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,44 @@
'use strict';
const Tab = require(__dirname + '/../components/tab.js');
class FunctionalTab extends Tab {
template() {
return `<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>`;
}
init() {
this.$el = this.generateNodeFromTemplate(this.template());
if (this.props.name !== 'Settings') {
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,78 @@
const { ipcRenderer } = require('electron');
const { shell, app } = require('electron').remote;
const LinkUtil = require('../utils/link-util');
const DomainUtil = require('../utils/domain-util');
const ConfigUtil = require('../utils/config-util');
const dingSound = new Audio('../resources/sounds/ding.ogg');
function handleExternalLink(event) {
const { url } = event;
const domainPrefix = DomainUtil.getDomain(this.props.index).url;
const downloadPath = ConfigUtil.getConfigItem('downloadsPath', `${app.getPath('downloads')}`);
const shouldShowInFolder = ConfigUtil.getConfigItem('showDownloadFolder', false);
// Whitelist URLs which are allowed to be opened in the app
const {
isInternalUrl: isWhiteListURL,
isUploadsUrl: isUploadsURL
} = LinkUtil.isInternal(domainPrefix, url);
if (isWhiteListURL) {
event.preventDefault();
// Show pdf attachments in a new window
if (LinkUtil.isPDF(url) && isUploadsURL) {
ipcRenderer.send('pdf-view', url);
return;
}
// download txt, mp3, mp4 etc.. by using downloadURL in the
// main process which allows the user to save the files to their desktop
// and not trigger webview reload while image in webview will
// do nothing and will not save it
if (!LinkUtil.isImage(url) && !LinkUtil.isPDF(url) && isUploadsURL) {
ipcRenderer.send('downloadFile', url, downloadPath);
ipcRenderer.once('downloadFileCompleted', (event, filePath, fileName) => {
const downloadNotification = new Notification('Download Complete', {
body: shouldShowInFolder ? `Click to show ${fileName} in folder` : `Click to open ${fileName}`,
silent: true // We'll play our own sound - ding.ogg
});
// Play sound to indicate download complete
if (!ConfigUtil.getConfigItem('silent')) {
dingSound.play();
}
downloadNotification.onclick = () => {
if (shouldShowInFolder) {
// Reveal file in download folder
shell.showItemInFolder(filePath);
} else {
// Open file in the default native app
shell.openItem(filePath);
}
};
ipcRenderer.removeAllListeners('downloadFileFailed');
});
ipcRenderer.once('downloadFileFailed', () => {
// Automatic download failed, so show save dialog prompt and download
// through webview
this.$el.downloadURL(url);
ipcRenderer.removeAllListeners('downloadFileCompleted');
});
return;
}
// open internal urls inside the current webview.
this.$el.loadURL(url);
} else {
event.preventDefault();
shell.openExternal(url);
}
}
module.exports = handleExternalLink;

View File

@@ -0,0 +1,60 @@
'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" 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>`;
}
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}`;
}
// Array index == Shown index - 1
ipcRenderer.send('switch-server-tab', shownIndex - 1);
return shortcutText;
}
}
module.exports = ServerTab;

View File

@@ -0,0 +1,48 @@
'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);
this.$el.addEventListener('mouseover', this.props.onHover);
this.$el.addEventListener('mouseout', this.props.onHoverOut);
}
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,248 @@
'use strict';
const path = require('path');
const fs = require('fs');
const ConfigUtil = require(__dirname + '/../utils/config-util.js');
const SystemUtil = require(__dirname + '/../utils/system-util.js');
const { app, dialog } = require('electron').remote;
const BaseComponent = require(__dirname + '/../components/base.js');
const handleExternalLink = require(__dirname + '/../components/handle-external-link.js');
const shouldSilentWebview = ConfigUtil.getConfigItem('silent');
class WebView extends BaseComponent {
constructor(props) {
super();
this.props = props;
this.zoomFactor = 1.0;
this.loading = true;
this.badgeCount = 0;
this.customCSS = ConfigUtil.getConfigItem('customCSS');
this.$webviewsContainer = document.querySelector('#webviews-container').classList;
}
template() {
return `<webview
class="disabled"
data-tab-id="${this.props.tabIndex}"
src="${this.props.url}"
${this.props.nodeIntegration ? 'nodeIntegration' : ''}
disablewebsecurity
${this.props.preload ? 'preload="js/preload.js"' : ''}
partition="persist:webviewsession"
name="${this.props.name}"
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 => {
handleExternalLink.call(this, event);
});
if (shouldSilentWebview) {
this.$el.addEventListener('dom-ready', () => {
this.$el.setAudioMuted(true);
});
}
this.$el.addEventListener('page-title-updated', event => {
const { title } = event;
this.badgeCount = this.getBadgeCount(title);
this.props.onTitleChange();
});
this.$el.addEventListener('did-navigate-in-page', event => {
const isSettingPage = event.url.includes('renderer/preference.html');
if (isSettingPage) {
return;
}
this.canGoBackButton();
});
this.$el.addEventListener('did-navigate', () => {
this.canGoBackButton();
});
this.$el.addEventListener('page-favicon-updated', event => {
const { favicons } = event;
// 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')) {
app.dock.bounce();
}
}
});
this.$el.addEventListener('dom-ready', () => {
if (this.props.role === 'server') {
this.$el.classList.add('onload');
}
this.loading = false;
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;
}
// To show or hide the loading indicator in the the active tab
if (this.loading) {
this.$webviewsContainer.remove('loaded');
} else {
this.$webviewsContainer.add('loaded');
}
this.$el.classList.remove('disabled');
this.$el.classList.add('active');
setTimeout(() => {
if (this.props.role === 'server') {
this.$el.classList.remove('onload');
}
}, 1000);
this.focus();
this.props.onTitleChange();
// Injecting preload css in webview to override some css rules
this.$el.insertCSS(fs.readFileSync(path.join(__dirname, '/../../css/preload.css'), 'utf8'));
// get customCSS again from config util to avoid warning user again
this.customCSS = ConfigUtil.getConfigItem('customCSS');
if (this.customCSS) {
if (!fs.existsSync(this.customCSS)) {
this.customCSS = null;
ConfigUtil.setConfigItem('customCSS', null);
const errMsg = 'The custom css previously set is deleted!';
dialog.showErrorBox('custom css file deleted!', errMsg);
return;
}
this.$el.insertCSS(fs.readFileSync(path.resolve(__dirname, this.customCSS), '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');
this.$el.classList.remove('active');
}
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();
}
}
canGoBackButton() {
const $backButton = document.querySelector('#actions-container #back-action');
if (this.$el.canGoBack()) {
$backButton.classList.remove('disable');
} else {
$backButton.classList.add('disable');
}
}
forward() {
if (this.$el.canGoForward()) {
this.$el.goForward();
}
}
reload() {
this.hide();
// Shows the loading indicator till the webview is reloaded
this.$webviewsContainer.remove('loaded');
this.loading = true;
this.$el.reload();
}
send(...param) {
this.$el.send(...param);
}
}
module.exports = WebView;

View File

@@ -0,0 +1,40 @@
const events = require('events');
const { ipcRenderer } = require('electron');
// we have and will have some non camelcase stuff
// while working with zulip so just turning the rule off
// for the whole file.
/* eslint-disable camelcase */
class ElectronBridge extends events {
send_event(...args) {
this.emit(...args);
}
on_event(...args) {
this.on(...args);
}
}
const electron_bridge = new ElectronBridge();
electron_bridge.on('total_unread_count', (...args) => {
ipcRenderer.send('unread-count', ...args);
});
electron_bridge.on('realm_name', realmName => {
const serverURL = location.origin;
ipcRenderer.send('realm-name-changed', serverURL, realmName);
});
electron_bridge.on('realm_icon_url', iconURL => {
const serverURL = location.origin;
iconURL = iconURL.includes('http') ? iconURL : `${serverURL}${iconURL}`;
ipcRenderer.send('realm-icon-changed', serverURL, iconURL);
});
// 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 ElectronBrigde.send_event
// which is alias of .emit and on this side we can handle the data by adding
// a listener for the event.
module.exports = electron_bridge;

View File

@@ -0,0 +1,62 @@
const { app } = require('electron').remote;
const path = require('path');
const fs = require('fs');
const SendFeedback = require('@electron-elements/send-feedback');
// make the button color match zulip app's theme
SendFeedback.customStyles = `
button:hover, button:focus {
border-color: #4EBFAC;
color: #4EBFAC;
}
button:active {
background-color: #f1f1f1;
color: #4EBFAC;
}
button {
background-color: #4EBFAC;
border-color: #4EBFAC;
}
`;
customElements.define('send-feedback', SendFeedback);
const sendFeedback = document.querySelector('send-feedback');
const feedbackHolder = sendFeedback.parentElement;
// customize the fields of custom elements
sendFeedback.title = 'Report Issue';
sendFeedback.titleLabel = 'Issue title:';
sendFeedback.titlePlaceholder = 'Enter issue title';
sendFeedback.textareaLabel = 'Describe the issue:';
sendFeedback.textareaPlaceholder = 'Succinctly describe your issue and steps to reproduce it...';
sendFeedback.buttonLabel = 'Report Issue';
sendFeedback.loaderSuccessText = '';
sendFeedback.useReporter('emailReporter', {
email: 'akash@zulipchat.com'
});
feedbackHolder.addEventListener('click', e => {
// only remove the class if the grey out faded
// part is clicked and not the feedback element itself
if (e.target === e.currentTarget) {
feedbackHolder.classList.remove('show');
}
});
sendFeedback.addEventListener('feedback-submitted', () => {
setTimeout(() => {
feedbackHolder.classList.remove('show');
}, 1000);
});
const dataDir = app.getPath('userData');
const logsDir = path.join(dataDir, '/Logs');
sendFeedback.logs.push(...fs.readdirSync(logsDir).map(file => path.join(logsDir, file)));
module.exports = {
feedbackHolder,
sendFeedback
};

686
app/renderer/js/main.js Normal file
View File

@@ -0,0 +1,686 @@
'use strict';
const { ipcRenderer, remote } = require('electron');
const isDev = require('electron-is-dev');
const { session, app } = remote;
const escape = require('escape-html');
require(__dirname + '/js/tray.js');
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');
const DNDUtil = require(__dirname + '/js/utils/dnd-util.js');
const ReconnectUtil = require(__dirname + '/js/utils/reconnect-util.js');
const Logger = require(__dirname + '/js/utils/logger-util.js');
const CommonUtil = require(__dirname + '/js/utils/common-util.js');
const { feedbackHolder } = require(__dirname + '/js/feedback.js');
const logger = new Logger({
file: 'errors.log',
timestamp: true
});
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.$backButton = $actionsContainer.querySelector('#back-action');
this.$dndButton = $actionsContainer.querySelector('#dnd-action');
this.$addServerTooltip = document.getElementById('add-server-tooltip');
this.$reloadTooltip = $actionsContainer.querySelector('#reload-tooltip');
this.$settingsTooltip = $actionsContainer.querySelector('#setting-tooltip');
this.$serverIconTooltip = document.getElementsByClassName('server-tooltip');
this.$backTooltip = $actionsContainer.querySelector('#back-tooltip');
this.$dndTooltip = $actionsContainer.querySelector('#dnd-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 = {};
this.tabIndex = 0;
}
init() {
this.loadProxy().then(() => {
this.initSidebar();
this.initTabs();
this.initActions();
this.registerIpcs();
this.initDefaultSettings();
});
}
loadProxy() {
return new Promise(resolve => {
// To change proxyEnable to useManualProxy in older versions
const proxyEnabledOld = ConfigUtil.isConfigItemExists('useProxy');
if (proxyEnabledOld) {
const proxyEnableOldState = ConfigUtil.getConfigItem('useProxy');
if (proxyEnableOldState) {
ConfigUtil.setConfigItem('useManualProxy', true);
}
ConfigUtil.removeConfigItem('useProxy');
}
const proxyEnabled = ConfigUtil.getConfigItem('useManualProxy') || ConfigUtil.getConfigItem('useSystemProxy');
if (proxyEnabled) {
session.fromPartition('persist:webviewsession').setProxy({
pacScript: ConfigUtil.getConfigItem('proxyPAC', ''),
proxyRules: ConfigUtil.getConfigItem('proxyRules', ''),
proxyBypassRules: ConfigUtil.getConfigItem('proxyBypass', '')
}, resolve);
} else {
session.fromPartition('persist:webviewsession').setProxy({
pacScript: '',
proxyRules: '',
proxyBypassRules: ''
}, resolve);
}
});
}
// 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 will make sure the default settings are correctly set to either true or false
initDefaultSettings() {
// Default settings which should be respected
const settingOptions = {
trayIcon: true,
useManualProxy: false,
useSystemProxy: false,
showSidebar: true,
badgeOption: true,
startAtLogin: false,
startMinimized: false,
enableSpellchecker: true,
showNotification: true,
autoUpdate: true,
betaUpdate: false,
customCSS: false,
silent: false,
lastActiveTab: 0,
dnd: false,
dndPreviousSettings: {
showNotification: true,
silent: false
},
downloadsPath: `${app.getPath('downloads')}`,
showDownloadFolder: false
};
// Platform specific settings
if (process.platform === 'win32') {
// Only available on Windows
settingOptions.flashTaskbarOnMessage = true;
settingOptions.dndPreviousSettings.flashTaskbarOnMessage = true;
}
if (process.platform === 'darwin') {
// Only available on macOS
settingOptions.dockBouncing = true;
}
if (process.platform !== 'darwin') {
settingOptions.autoHideMenubar = false;
}
for (const i in settingOptions) {
if (ConfigUtil.getConfigItem(i) === null) {
ConfigUtil.setConfigItem(i, settingOptions[i]);
}
}
}
initSidebar() {
const showSidebar = ConfigUtil.getConfigItem('showSidebar', 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);
DomainUtil.updateSavedServer(servers[i].url, i);
this.activateTab(i);
}
// Open last active tab
this.activateTab(ConfigUtil.getConfigItem('lastActiveTab'));
// Remove focus from the settings icon at sidebar bottom
this.$settingsButton.classList.remove('active');
} else {
this.openSettings('AddServer');
}
}
initServer(server, index) {
const tabIndex = this.getTabIndex();
this.tabs.push(new ServerTab({
role: 'server',
icon: server.icon,
name: server.alias,
$root: this.$tabsContainer,
onClick: this.activateLastTab.bind(this, index),
index,
tabIndex,
onHover: this.onHover.bind(this, index),
onHoverOut: this.onHoverOut.bind(this, index),
webview: new WebView({
$root: this.$webviewsContainer,
index,
tabIndex,
url: server.url,
name: CommonUtil.decodeString(server.alias),
isActive: () => {
return index === this.activeTabIndex;
},
onNetworkError: this.openNetworkTroubleshooting.bind(this),
onTitleChange: this.updateBadge.bind(this),
nodeIntegration: false,
preload: true
})
}));
}
initActions() {
this.initDNDButton();
this.$dndButton.addEventListener('click', () => {
const dndUtil = DNDUtil.toggle();
ipcRenderer.send('forward-message', 'toggle-dnd', dndUtil.dnd, dndUtil.newSettings);
});
this.$reloadButton.addEventListener('click', () => {
this.tabs[this.activeTabIndex].webview.reload();
});
this.$addServerButton.addEventListener('click', () => {
this.openSettings('AddServer');
});
this.$settingsButton.addEventListener('click', () => {
this.openSettings('General');
});
this.$backButton.addEventListener('click', () => {
this.tabs[this.activeTabIndex].webview.back();
});
const $serverImgs = document.querySelectorAll('.server-icons');
$serverImgs.forEach($serverImg => {
if ($serverImg.src.includes('img/icon.png')) {
this.displayInitalCharLogo($serverImg);
}
$serverImg.addEventListener('error', () => {
this.displayInitalCharLogo($serverImg);
});
});
this.sidebarHoverEvent(this.$addServerButton, this.$addServerTooltip, true);
this.sidebarHoverEvent(this.$settingsButton, this.$settingsTooltip);
this.sidebarHoverEvent(this.$reloadButton, this.$reloadTooltip);
this.sidebarHoverEvent(this.$backButton, this.$backTooltip);
this.sidebarHoverEvent(this.$dndButton, this.$dndTooltip);
}
initDNDButton() {
const dnd = ConfigUtil.getConfigItem('dnd', false);
this.toggleDNDButton(dnd);
}
getTabIndex() {
const currentIndex = this.tabIndex;
this.tabIndex++;
return currentIndex;
}
displayInitalCharLogo($img) {
const $altIcon = document.createElement('div');
const $parent = $img.parentElement;
const $container = $parent.parentElement;
const webviewId = $container.dataset.tabId;
const $webview = document.querySelector(`webview[data-tab-id="${webviewId}"]`);
const realmName = $webview.getAttribute('name');
if (realmName === null) {
$img.src = '/img/icon.png';
return;
}
$altIcon.textContent = realmName.charAt(0) || 'Z';
$altIcon.classList.add('server-icon');
$altIcon.classList.add('alt-icon');
$parent.removeChild($img);
$parent.appendChild($altIcon);
}
sidebarHoverEvent(SidebarButton, SidebarTooltip, addServer = false) {
SidebarButton.addEventListener('mouseover', () => {
SidebarTooltip.removeAttribute('style');
// To handle position of add server tooltip due to scrolling of list of organizations
// This could not be handled using CSS, hence the top of the tooltip is made same
// as that of its parent element.
// This needs to handled only for the add server tooltip and not others.
if (addServer) {
const { top } = SidebarButton.getBoundingClientRect();
SidebarTooltip.style.top = top + 'px';
}
});
SidebarButton.addEventListener('mouseout', () => {
SidebarTooltip.style.display = 'none';
});
}
onHover(index) {
// this.$serverIconTooltip[index].innerHTML already has realm name, so we are just
// removing the style.
this.$serverIconTooltip[index].removeAttribute('style');
// To handle position of servers' tooltip due to scrolling of list of organizations
// This could not be handled using CSS, hence the top of the tooltip is made same
// as that of its parent element.
const { top } = this.$serverIconTooltip[index].parentElement.getBoundingClientRect();
this.$serverIconTooltip[index].style.top = top + 'px';
}
onHoverOut(index) {
this.$serverIconTooltip[index].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;
const tabIndex = this.getTabIndex();
this.tabs.push(new FunctionalTab({
role: 'function',
materialIcon: tabProps.materialIcon,
name: tabProps.name,
$root: this.$tabsContainer,
index: this.functionalTabs[tabProps.name],
tabIndex,
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],
tabIndex,
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
})
}));
// To show loading indicator the first time a functional tab is opened, indicator is
// closed when the functional tab DOM is ready, handled in webview.js
this.$webviewsContainer.classList.remove('loaded');
this.activateTab(this.functionalTabs[tabProps.name]);
}
openSettings(nav = 'General') {
this.openFunctionalTab({
name: 'Settings',
materialIcon: 'settings',
url: `file://${__dirname}/preference.html#${nav}`
});
this.$settingsButton.classList.add('active');
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`
});
}
activateLastTab(index) {
// Open all the tabs in background, also activate the tab based on the index
this.activateTab(index);
// Save last active tab
ConfigUtil.setConfigItem('lastActiveTab', index);
}
activateTab(index, hideOldTab = true) {
if (!this.tabs[index]) {
return;
}
if (this.activeTabIndex !== -1) {
if (this.activeTabIndex === index) {
return;
} else if (hideOldTab) {
// If old tab is functional tab Settings, remove focus from the settings icon at sidebar bottom
if (this.tabs[this.activeTabIndex].props.role === 'function' && this.tabs[this.activeTabIndex].props.name === 'Settings') {
this.$settingsButton.classList.remove('active');
}
this.tabs[this.activeTabIndex].deactivate();
}
}
try {
this.tabs[index].webview.canGoBackButton();
} catch (err) {
}
this.activeTabIndex = index;
this.tabs[index].activate();
ipcRenderer.send('update-menu', {
tabs: this.tabs,
activeTabIndex: this.activeTabIndex,
// Following flag controls whether a menu item should be enabled or not
enableMenu: this.tabs[index].props.role === 'server'
});
}
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() {
// Show loading indicator
this.$webviewsContainer.classList.remove('loaded');
// Clear global variables
this.activeTabIndex = -1;
this.tabs = [];
this.functionalTabs = {};
// Clear DOM elements
this.$tabsContainer.innerHTML = '';
this.$webviewsContainer.innerHTML = '';
}
reloadView() {
// Save and remember the index of last active tab so that we can use it later
const lastActiveTab = this.tabs[this.activeTabIndex].props.index;
ConfigUtil.setConfigItem('lastActiveTab', lastActiveTab);
// Destroy the current view and re-initiate it
this.destroyView();
this.initTabs();
}
// This will trigger when pressed CTRL/CMD + R [WIP]
// It won't reload the current view properly when you add/delete a server.
reloadCurrentView() {
this.$reloadButton.click();
}
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('sidebar-hide');
} else {
this.$sidebar.classList.add('sidebar-hide');
}
}
// Toggles the dnd button icon.
toggleDNDButton(alert) {
this.$dndTooltip.textContent = (alert ? 'Turn Off' : 'Enable') + ' Do Not Disturb';
this.$dndButton.querySelector('i').textContent = alert ? 'notifications_off' : 'notifications';
}
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, this.tabs[this.activeTabIndex].props.index));
ipcRenderer.on('reload-current-viewer', this.reloadCurrentView.bind(this));
ipcRenderer.on('hard-reload', () => {
ipcRenderer.send('reload-full-app');
});
ipcRenderer.on('clear-app-data', () => {
ipcRenderer.send('clear-app-settings');
});
ipcRenderer.on('switch-server-tab', (event, index) => {
this.activateLastTab(index);
});
ipcRenderer.on('reload-proxy', (event, showAlert) => {
this.loadProxy().then(() => {
if (showAlert) {
alert('Proxy settings saved!');
ipcRenderer.send('reload-full-app');
}
});
});
ipcRenderer.on('toggle-sidebar', (event, show) => {
// Toggle the left sidebar
this.toggleSidebar(show);
// Toggle sidebar switch in the general settings
const selector = 'webview:not([class*=disabled])';
const webview = document.querySelector(selector);
const webContents = webview.getWebContents();
webContents.send('toggle-sidebar-setting', show);
});
ipcRenderer.on('toggle-silent', (event, state) => {
const webviews = document.querySelectorAll('webview');
webviews.forEach(webview => {
try {
webview.setAudioMuted(state);
} catch (err) {
// webview is not ready yet
webview.addEventListener('dom-ready', () => {
webview.setAudioMuted(state);
});
}
});
});
ipcRenderer.on('toggle-dnd', (event, state, newSettings) => {
this.toggleDNDButton(state);
ipcRenderer.send('forward-message', 'toggle-silent', newSettings.silent);
const selector = 'webview:not([class*=disabled])';
const webview = document.querySelector(selector);
const webContents = webview.getWebContents();
webContents.send('toggle-dnd', state, newSettings);
});
ipcRenderer.on('update-realm-name', (event, serverURL, realmName) => {
DomainUtil.getDomains().forEach((domain, index) => {
if (domain.url.includes(serverURL)) {
const serverTooltipSelector = `.tab .server-tooltip`;
const serverTooltips = document.querySelectorAll(serverTooltipSelector);
serverTooltips[index].innerHTML = escape(realmName);
this.tabs[index].props.name = escape(realmName);
this.tabs[index].webview.props.name = realmName;
domain.alias = escape(realmName);
DomainUtil.db.push(`/domains[${index}]`, domain, true);
DomainUtil.reloadDB();
// Update the realm name also on the Window menu
ipcRenderer.send('update-menu', {
tabs: this.tabs,
activeTabIndex: this.activeTabIndex
});
}
});
});
ipcRenderer.on('update-realm-icon', (event, serverURL, iconURL) => {
DomainUtil.getDomains().forEach((domain, index) => {
if (domain.url.includes(serverURL)) {
DomainUtil.saveServerIcon(iconURL).then(localIconUrl => {
const serverImgsSelector = `.tab .server-icons`;
const serverImgs = document.querySelectorAll(serverImgsSelector);
serverImgs[index].src = localIconUrl;
domain.icon = localIconUrl;
DomainUtil.db.push(`/domains[${index}]`, domain, true);
DomainUtil.reloadDB();
});
}
});
});
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('focus-webview-with-id', (event, webviewId) => {
const webviews = document.querySelectorAll('webview');
webviews.forEach(webview => {
const currentId = webview.getWebContents().id;
const tabId = webview.getAttribute('data-tab-id');
const concurrentTab = document.querySelector(`div[data-tab-id="${tabId}"]`);
if (currentId === webviewId) {
concurrentTab.click();
}
});
});
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));
});
ipcRenderer.on('open-feedback-modal', () => {
feedbackHolder.classList.add('show');
});
}
}
window.onload = () => {
const serverManagerView = new ServerManagerView();
const reconnectUtil = new ReconnectUtil(serverManagerView);
serverManagerView.init();
window.addEventListener('online', () => {
reconnectUtil.pollInternetAndReload();
});
window.addEventListener('offline', () => {
reconnectUtil.clearState();
logger.log('No internet connection, you are offline.');
});
// only start electron-connect (auto reload on change) when its ran
// from `npm run dev` or `gulp dev` and not from `npm start` when
// app is started `npm start` main process's proces.argv will have
// `--no-electron-connect`
const mainProcessArgv = remote.getGlobal('process').argv;
if (isDev && !mainProcessArgv.includes('--no-electron-connect')) {
const electronConnect = require('electron-connect');
electronConnect.client.create();
}
};

View File

@@ -0,0 +1,100 @@
'use strict';
const { ipcRenderer } = require('electron');
const url = require('url');
const MacNotifier = require('node-mac-notifier');
const ConfigUtil = require('../utils/config-util');
const {
appId, customReply, focusCurrentServer, parseReply, setupReply
} = require('./helpers');
let replyHandler;
let clickHandler;
class DarwinNotification {
constructor(title, opts) {
const silent = ConfigUtil.getConfigItem('silent') || false;
const { host, protocol } = location;
const { icon } = opts;
const profilePic = url.resolve(`${protocol}//${host}`, icon);
this.tag = opts.tag;
const notification = new MacNotifier(title, Object.assign(opts, {
bundleId: appId,
canReply: true,
silent,
icon: profilePic
}));
notification.addEventListener('click', () => {
// focus to the server who sent the
// notification if not focused already
if (clickHandler) {
clickHandler();
}
focusCurrentServer();
ipcRenderer.send('focus-app');
});
notification.addEventListener('reply', this.notificationHandler);
}
static requestPermission() {
return; // eslint-disable-line no-useless-return
}
// Override default Notification permission
static get permission() {
return ConfigUtil.getConfigItem('showNotification') ? 'granted' : 'denied';
}
set onreply(handler) {
replyHandler = handler;
}
get onreply() {
return replyHandler;
}
set onclick(handler) {
clickHandler = handler;
}
get onclick() {
return clickHandler;
}
// not something that is common or
// used by zulip server but added to be
// future proff.
addEventListener(event, handler) {
if (event === 'click') {
clickHandler = handler;
}
if (event === 'reply') {
replyHandler = handler;
}
}
notificationHandler({ response }) {
response = parseReply(response);
focusCurrentServer();
setupReply(this.tag);
if (replyHandler) {
replyHandler(response);
return;
}
customReply(response);
}
// method specific to notification api
// used by zulip
close() {
return; // eslint-disable-line no-useless-return
}
}
module.exports = DarwinNotification;

View File

@@ -0,0 +1,31 @@
'use strict';
const { ipcRenderer } = require('electron');
const ConfigUtil = require('../utils/config-util');
const { focusCurrentServer } = require('./helpers');
const NativeNotification = window.Notification;
class BaseNotification extends NativeNotification {
constructor(title, opts) {
opts.silent = true;
super(title, opts);
this.addEventListener('click', () => {
// focus to the server who sent the
// notification if not focused already
focusCurrentServer();
ipcRenderer.send('focus-app');
});
}
static requestPermission() {
return; // eslint-disable-line no-useless-return
}
// Override default Notification permission
static get permission() {
return ConfigUtil.getConfigItem('showNotification') ? 'granted' : 'denied';
}
}
module.exports = BaseNotification;

View File

@@ -0,0 +1,155 @@
const { remote } = require('electron');
const Logger = require('../utils/logger-util.js');
const logger = new Logger({
file: 'errors.log',
timestamp: true
});
// Do not change this
const appId = 'org.zulip.zulip-electron';
const botsList = [];
let botsListLoaded = false;
// this function load list of bots from the server
// sync=True for a synchronous getJSON request
// in case botsList isn't already completely loaded when required in parseRely
function loadBots(sync = false) {
const { $ } = window;
botsList.length = 0;
if (sync) {
$.ajaxSetup({async: false});
}
$.getJSON('/json/users')
.done(data => {
const members = data.members;
members.forEach(membersRow => {
if (membersRow.is_bot) {
const bot = `@${membersRow.full_name}`;
const mention = `@**${bot.replace(/^@/, '')}**`;
botsList.push([bot, mention]);
}
});
botsListLoaded = true;
})
.fail(error => {
logger.log('Load bots request failed: ', error.responseText);
logger.log('Load bots request status: ', error.statusText);
});
if (sync) {
$.ajaxSetup({async: true});
}
}
function checkElements(...elements) {
let status = true;
elements.forEach(element => {
if (element === null || element === undefined) {
status = false;
}
});
return status;
}
function customReply(reply) {
// server does not support notification reply yet.
const buttonSelector = '.messagebox #send_controls button[type=submit]';
const messageboxSelector = '.selected_message .messagebox .messagebox-border .messagebox-content';
const textarea = document.querySelector('#compose-textarea');
const messagebox = document.querySelector(messageboxSelector);
const sendButton = document.querySelector(buttonSelector);
// sanity check for old server versions
const elementsExists = checkElements(textarea, messagebox, sendButton);
if (!elementsExists) {
return;
}
textarea.value = reply;
messagebox.click();
sendButton.click();
}
const currentWindow = remote.getCurrentWindow();
const webContents = remote.getCurrentWebContents();
const webContentsId = webContents.id;
// this function will focus the server that sent
// the notification. Main function implemented in main.js
function focusCurrentServer() {
currentWindow.send('focus-webview-with-id', webContentsId);
}
// this function parses the reply from to notification
// making it easier to reply from notification eg
// @username in reply will be converted to @**username**
// #stream in reply will be converted to #**stream**
// bot mentions are not yet supported
function parseReply(reply) {
const usersDiv = document.querySelectorAll('#user_presences li');
const streamHolder = document.querySelectorAll('#stream_filters li');
const users = [];
const streams = [];
usersDiv.forEach(userRow => {
const anchor = userRow.querySelector('span a');
if (anchor !== null) {
const user = `@${anchor.textContent.trim()}`;
const mention = `@**${user.replace(/^@/, '')}**`;
users.push([user, mention]);
}
});
streamHolder.forEach(stream => {
const streamAnchor = stream.querySelector('div a');
if (streamAnchor !== null) {
const streamName = `#${streamAnchor.textContent.trim()}`;
const streamMention = `#**${streamName.replace(/^#/, '')}**`;
streams.push([streamName, streamMention]);
}
});
users.forEach(([user, mention]) => {
if (reply.includes(user)) {
const regex = new RegExp(user, 'g');
reply = reply.replace(regex, mention);
}
});
streams.forEach(([stream, streamMention]) => {
const regex = new RegExp(stream, 'g');
reply = reply.replace(regex, streamMention);
});
// If botsList isn't completely loaded yet, make a synchronous getJSON request for list
if (botsListLoaded === false) {
loadBots(true);
}
// Iterate for every bot name and replace in reply
// @botname with @**botname**
botsList.forEach(([bot, mention]) => {
if (reply.includes(bot)) {
const regex = new RegExp(bot, 'g');
reply = reply.replace(regex, mention);
}
});
reply = reply.replace(/\\n/, '\n');
return reply;
}
function setupReply(id) {
const { narrow } = window;
narrow.by_subject(id, { trigger: 'notification' });
}
module.exports = {
appId,
checkElements,
customReply,
parseReply,
setupReply,
focusCurrentServer,
loadBots
};

View File

@@ -0,0 +1,27 @@
'use strict';
const {
remote: { app }
} = require('electron');
const params = require('../utils/params-util.js');
const DefaultNotification = require('./default-notification');
const { appId, loadBots } = require('./helpers');
// 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(appId);
window.Notification = DefaultNotification;
if (process.platform === 'darwin') {
const DarwinNotification = require('./darwin-notifications');
window.Notification = DarwinNotification;
}
window.addEventListener('load', () => {
// eslint-disable-next-line no-undef, camelcase
if (params.isPageParams() && page_params.realm_uri) {
loadBots();
}
});

View File

@@ -0,0 +1,20 @@
'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,91 @@
'use-strict';
const { dialog } = require('electron').remote;
const BaseComponent = require(__dirname + '/../../components/base.js');
const CertificateUtil = require(__dirname + '/../../utils/certificate-util.js');
const DomainUtil = require(__dirname + '/../../utils/domain-util.js');
class AddCertificate extends BaseComponent {
constructor(props) {
super();
this.props = props;
this._certFile = '';
}
template() {
return `
<div class="settings-card certificates-card">
<div class="certificate-input">
<div>Organization URL</div>
<input class="setting-input-value" autofocus placeholder="your-organization.zulipchat.com or zulip.your-organization.com"/>
</div>
<div class="certificate-input">
<div>Certificate file</div>
<button class="green" id="add-certificate-button">Upload</button>
</div>
</div>
`;
}
init() {
this.$addCertificate = this.generateNodeFromTemplate(this.template());
this.props.$root.appendChild(this.$addCertificate);
this.addCertificateButton = this.$addCertificate.querySelector('#add-certificate-button');
this.serverUrl = this.$addCertificate.querySelectorAll('input.setting-input-value')[0];
this.initListeners();
}
validateAndAdd() {
const certificate = this._certFile;
const serverUrl = this.serverUrl.value;
if (certificate !== '' && serverUrl !== '') {
const server = encodeURIComponent(DomainUtil.formatUrl(serverUrl));
const fileName = certificate.substring(certificate.lastIndexOf('/') + 1);
const copy = CertificateUtil.copyCertificate(server, certificate, fileName);
if (!copy) {
return;
}
CertificateUtil.setCertificate(server, fileName);
dialog.showMessageBox({
title: 'Success',
message: `Certificate saved!`
});
this.serverUrl.value = '';
} else {
dialog.showErrorBox('Error', `Please, ${serverUrl === '' ?
'Enter an Organization URL' : 'Choose certificate file'}`);
}
}
addHandler() {
const showDialogOptions = {
title: 'Select file',
defaultId: 1,
properties: ['openFile'],
filters: [{ name: 'crt, pem', extensions: ['crt', 'pem'] }]
};
dialog.showOpenDialog(showDialogOptions, selectedFile => {
if (selectedFile) {
this._certFile = selectedFile[0] || '';
this.validateAndAdd();
}
});
}
initListeners() {
this.addCertificateButton.addEventListener('click', () => {
this.addHandler();
});
this.serverUrl.addEventListener('keypress', event => {
const EnterkeyCode = event.keyCode;
if (EnterkeyCode === 13) {
this.addHandler();
}
});
}
}
module.exports = AddCertificate;

View File

@@ -0,0 +1,63 @@
'use strict';
const electron = require('electron');
const { app } = require('electron');
const ConfigUtil = require(__dirname + '/../../utils/config-util.js');
let instance = null;
class BadgeSettings {
constructor() {
if (instance) {
return instance;
} else {
instance = this;
}
return instance;
}
showBadgeCount(messageCount, mainWindow) {
if (process.platform === 'darwin') {
app.setBadgeCount(messageCount);
}
if (process.platform === 'win32') {
this.updateOverlayIcon(messageCount, mainWindow);
}
}
hideBadgeCount(mainWindow) {
if (process.platform === 'darwin') {
app.setBadgeCount(0);
}
if (process.platform === 'win32') {
mainWindow.setOverlayIcon(null, '');
}
}
updateBadge(badgeCount, mainWindow) {
if (ConfigUtil.getConfigItem('badgeOption', true)) {
this.showBadgeCount(badgeCount, mainWindow);
} else {
this.hideBadgeCount(mainWindow);
}
}
updateOverlayIcon(messageCount, mainWindow) {
if (!mainWindow.isFocused()) {
mainWindow.flashFrame(ConfigUtil.getConfigItem('flashTaskbarOnMessage'));
}
if (messageCount === 0) {
mainWindow.setOverlayIcon(null, '');
} else {
mainWindow.webContents.send('render-taskbar-icon', messageCount);
}
}
updateTaskbarIcon(data, text, mainWindow) {
const img = electron.nativeImage.createFromDataURL(data);
mainWindow.setOverlayIcon(img, text);
}
}
module.exports = new BadgeSettings();

View File

@@ -0,0 +1,46 @@
'use strict';
const {ipcRenderer} = require('electron');
const BaseComponent = require(__dirname + '/../../components/base.js');
class BaseSection extends BaseComponent {
generateSettingOption(props) {
const {$element, value, clickHandler} = props;
$element.innerHTML = '';
const $optionControl = this.generateNodeFromTemplate(this.generateOptionTemplate(value));
$element.appendChild($optionControl);
$optionControl.addEventListener('click', clickHandler);
}
generateOptionTemplate(settingOption) {
if (settingOption) {
return `
<div class="action">
<div class="switch">
<input class="toggle toggle-round" type="checkbox" checked>
<label></label>
</div>
</div>
`;
} else {
return `
<div class="action">
<div class="switch">
<input class="toggle toggle-round" type="checkbox">
<label></label>
</div>
</div>
`;
}
}
reloadApp() {
ipcRenderer.send('forward-message', 'reload-viewer');
}
}
module.exports = BaseSection;

View File

@@ -0,0 +1,64 @@
'use strict';
const BaseSection = require(__dirname + '/base-section.js');
const DomainUtil = require(__dirname + '/../../utils/domain-util.js');
const ServerInfoForm = require(__dirname + '/server-info-form.js');
const AddCertificate = require(__dirname + '/add-certificate.js');
class ConnectedOrgSection extends BaseSection {
constructor(props) {
super();
this.props = props;
}
template() {
return `
<div class="settings-pane" id="server-settings-pane">
<div class="page-title">Connected organizations</div>
<div class="title" id="existing-servers">All the connected orgnizations will appear here.</div>
<div id="server-info-container"></div>
<div class="page-title">Add Custom Certificates</div>
<div id="add-certificate-container"></div>
</div>
`;
}
init() {
this.initServers();
}
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');
const noServerText = 'All the connected orgnizations will appear here';
// Show noServerText if no servers are there otherwise hide it
this.$existingServers.innerHTML = servers.length === 0 ? noServerText : '';
for (let i = 0; i < servers.length; i++) {
new ServerInfoForm({
$root: this.$serverInfoContainer,
server: servers[i],
index: i,
onChange: this.reloadApp
}).init();
}
this.$addCertificateContainer = document.getElementById('add-certificate-container');
this.initAddCertificate();
}
initAddCertificate() {
new AddCertificate({
$root: this.$addCertificateContainer
}).init();
}
}
module.exports = ConnectedOrgSection;

View File

@@ -0,0 +1,427 @@
'use strict';
const path = require('path');
const { ipcRenderer, remote } = require('electron');
const fs = require('fs-extra');
const { app, dialog } = remote;
const currentBrowserWindow = remote.getCurrentWindow();
const BaseSection = require(__dirname + '/base-section.js');
const ConfigUtil = require(__dirname + '/../../utils/config-util.js');
class GeneralSection extends BaseSection {
constructor(props) {
super();
this.props = props;
}
template() {
return `
<div class="settings-pane">
<div class="title">Appearance</div>
<div id="appearance-option-settings" class="settings-card">
<div class="setting-row" id="tray-option">
<div class="setting-description">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">Auto hide Menubar (Press Alt key to display)</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="sidebar-option">
<div class="setting-description">Show sidebar (<span class="code">Cmd/Ctrl+S</span>)</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="badge-option">
<div class="setting-description">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">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">Flash taskbar on new message</div>
<div class="setting-control"></div>
</div>
</div>
<div class="title">Desktop Notifications</div>
<div class="settings-card">
<div class="setting-row" id="show-notification-option">
<div class="setting-description">Show Desktop Notifications</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="silent-option">
<div class="setting-description">Mute all sounds from Zulip</div>
<div class="setting-control"></div>
</div>
</div>
<div class="title">App Updates</div>
<div class="settings-card">
<div class="setting-row" id="autoupdate-option">
<div class="setting-description">Enable auto updates</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="betaupdate-option">
<div class="setting-description">Get beta updates</div>
<div class="setting-control"></div>
</div>
</div>
<div class="title">Functionality</div>
<div class="settings-card">
<div class="setting-row" id="startAtLogin-option">
<div class="setting-description">Start app at login</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="start-minimize-option">
<div class="setting-description">Always start minimized</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="enable-spellchecker-option">
<div class="setting-description">Enable spellchecker (requires restart)</div>
<div class="setting-control"></div>
</div>
</div>
<div class="title">Advanced</div>
<div class="settings-card">
<div class="setting-row" id="show-download-folder">
<div class="setting-description">Show downloaded files in file manager</div>
<div class="setting-control"></div>
</div>
<div class="setting-row" id="add-custom-css">
<div class="setting-description">
Add custom CSS
</div>
<button class="custom-css-button green">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>Delete</span>
</div>
</div>
<div class="setting-row" id="download-folder">
<div class="setting-description">
Default download location
</div>
<button class="download-folder-button green">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>
<div class="title">Reset Application Data</div>
<div class="settings-card">
<div class="setting-row" id="resetdata-option">
<div class="setting-description">This will delete all application data including all added accounts and preferences
</div>
<button class="reset-data-button green w-150">Reset App Data</button>
</div>
</div>
</div>
`;
}
init() {
this.props.$root.innerHTML = this.template();
this.updateTrayOption();
this.updateBadgeOption();
this.updateSilentOption();
this.autoUpdateOption();
this.betaUpdateOption();
this.updateSidebarOption();
this.updateStartAtLoginOption();
this.updateResetDataOption();
this.showDesktopNotification();
this.enableSpellchecker();
this.minimizeOnStart();
this.addCustomCSS();
this.showCustomCSSPath();
this.removeCustomCSS();
this.downloadFolder();
this.showDownloadFolder();
// Platform specific settings
// Flashing taskbar on Windows
if (process.platform === 'win32') {
this.updateFlashTaskbar();
}
// Dock bounce on macOS
if (process.platform === 'darwin') {
this.updateDockBouncing();
}
// Auto hide menubar on Windows and Linux
if (process.platform !== 'darwin') {
this.updateMenubarOption();
}
}
updateTrayOption() {
this.generateSettingOption({
$element: document.querySelector('#tray-option .setting-control'),
value: ConfigUtil.getConfigItem('trayIcon', true),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('trayIcon');
ConfigUtil.setConfigItem('trayIcon', newValue);
ipcRenderer.send('forward-message', 'toggletray');
this.updateTrayOption();
}
});
}
updateMenubarOption() {
this.generateSettingOption({
$element: document.querySelector('#menubar-option .setting-control'),
value: ConfigUtil.getConfigItem('autoHideMenubar', false),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('autoHideMenubar');
ConfigUtil.setConfigItem('autoHideMenubar', newValue);
ipcRenderer.send('toggle-menubar', newValue);
this.updateMenubarOption();
}
});
}
updateBadgeOption() {
this.generateSettingOption({
$element: document.querySelector('#badge-option .setting-control'),
value: ConfigUtil.getConfigItem('badgeOption', true),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('badgeOption');
ConfigUtil.setConfigItem('badgeOption', newValue);
ipcRenderer.send('toggle-badge-option', newValue);
this.updateBadgeOption();
}
});
}
updateDockBouncing() {
this.generateSettingOption({
$element: document.querySelector('#dock-bounce-option .setting-control'),
value: ConfigUtil.getConfigItem('dockBouncing', true),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('dockBouncing');
ConfigUtil.setConfigItem('dockBouncing', newValue);
this.updateDockBouncing();
}
});
}
updateFlashTaskbar() {
this.generateSettingOption({
$element: document.querySelector('#flash-taskbar-option .setting-control'),
value: ConfigUtil.getConfigItem('flashTaskbarOnMessage', true),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('flashTaskbarOnMessage');
ConfigUtil.setConfigItem('flashTaskbarOnMessage', newValue);
this.updateFlashTaskbar();
}
});
}
autoUpdateOption() {
this.generateSettingOption({
$element: document.querySelector('#autoupdate-option .setting-control'),
value: ConfigUtil.getConfigItem('autoUpdate', true),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('autoUpdate');
ConfigUtil.setConfigItem('autoUpdate', newValue);
this.autoUpdateOption();
}
});
}
betaUpdateOption() {
this.generateSettingOption({
$element: document.querySelector('#betaupdate-option .setting-control'),
value: ConfigUtil.getConfigItem('betaUpdate', false),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('betaUpdate');
ConfigUtil.setConfigItem('betaUpdate', newValue);
this.betaUpdateOption();
}
});
}
updateSilentOption() {
this.generateSettingOption({
$element: document.querySelector('#silent-option .setting-control'),
value: ConfigUtil.getConfigItem('silent', false),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('silent', true);
ConfigUtil.setConfigItem('silent', newValue);
this.updateSilentOption();
currentBrowserWindow.send('toggle-silent', newValue);
}
});
}
showDesktopNotification() {
this.generateSettingOption({
$element: document.querySelector('#show-notification-option .setting-control'),
value: ConfigUtil.getConfigItem('showNotification', true),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('showNotification', true);
ConfigUtil.setConfigItem('showNotification', newValue);
this.showDesktopNotification();
}
});
}
updateSidebarOption() {
this.generateSettingOption({
$element: document.querySelector('#sidebar-option .setting-control'),
value: ConfigUtil.getConfigItem('showSidebar', true),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('showSidebar');
ConfigUtil.setConfigItem('showSidebar', newValue);
ipcRenderer.send('forward-message', 'toggle-sidebar', newValue);
this.updateSidebarOption();
}
});
}
updateStartAtLoginOption() {
this.generateSettingOption({
$element: document.querySelector('#startAtLogin-option .setting-control'),
value: ConfigUtil.getConfigItem('startAtLogin', false),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('startAtLogin');
ConfigUtil.setConfigItem('startAtLogin', newValue);
ipcRenderer.send('toggleAutoLauncher', newValue);
this.updateStartAtLoginOption();
}
});
}
enableSpellchecker() {
this.generateSettingOption({
$element: document.querySelector('#enable-spellchecker-option .setting-control'),
value: ConfigUtil.getConfigItem('enableSpellchecker', true),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('enableSpellchecker');
ConfigUtil.setConfigItem('enableSpellchecker', newValue);
this.enableSpellchecker();
}
});
}
clearAppDataDialog() {
const clearAppDataMessage = 'By clicking proceed you will be removing all added accounts and preferences from Zulip. When the application restarts, it will be as if you are starting Zulip for the first time.';
const getAppPath = path.join(app.getPath('appData'), app.getName());
dialog.showMessageBox({
type: 'warning',
buttons: ['YES', 'NO'],
defaultId: 0,
message: 'Are you sure',
detail: clearAppDataMessage
}, response => {
if (response === 0) {
fs.remove(getAppPath);
setTimeout(() => ipcRenderer.send('forward-message', 'hard-reload'), 1000);
}
});
}
customCssDialog() {
const showDialogOptions = {
title: 'Select file',
defaultId: 1,
properties: ['openFile'],
filters: [{ name: 'CSS file', extensions: ['css'] }]
};
dialog.showOpenDialog(showDialogOptions, selectedFile => {
if (selectedFile) {
ConfigUtil.setConfigItem('customCSS', selectedFile[0]);
ipcRenderer.send('forward-message', 'hard-reload');
}
});
}
updateResetDataOption() {
const resetDataButton = document.querySelector('#resetdata-option .reset-data-button');
resetDataButton.addEventListener('click', () => {
this.clearAppDataDialog();
});
}
minimizeOnStart() {
this.generateSettingOption({
$element: document.querySelector('#start-minimize-option .setting-control'),
value: ConfigUtil.getConfigItem('startMinimized', false),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('startMinimized');
ConfigUtil.setConfigItem('startMinimized', newValue);
this.minimizeOnStart();
}
});
}
addCustomCSS() {
const customCSSButton = document.querySelector('#add-custom-css .custom-css-button');
customCSSButton.addEventListener('click', () => {
this.customCssDialog();
});
}
showCustomCSSPath() {
if (!ConfigUtil.getConfigItem('customCSS')) {
const cssPATH = document.getElementById('remove-custom-css');
cssPATH.style.display = 'none';
}
}
removeCustomCSS() {
const removeCSSButton = document.getElementById('css-delete-action');
removeCSSButton.addEventListener('click', () => {
ConfigUtil.setConfigItem('customCSS');
ipcRenderer.send('forward-message', 'hard-reload');
});
}
downloadFolderDialog() {
const showDialogOptions = {
title: 'Select Download Location',
defaultId: 1,
properties: ['openDirectory']
};
dialog.showOpenDialog(showDialogOptions, selectedFolder => {
if (selectedFolder) {
ConfigUtil.setConfigItem('downloadsPath', selectedFolder[0]);
const downloadFolderPath = document.querySelector('.download-folder-path');
downloadFolderPath.innerText = selectedFolder[0];
}
});
}
downloadFolder() {
const downloadFolder = document.querySelector('#download-folder .download-folder-button');
downloadFolder.addEventListener('click', () => {
this.downloadFolderDialog();
});
}
showDownloadFolder() {
this.generateSettingOption({
$element: document.querySelector('#show-download-folder .setting-control'),
value: ConfigUtil.getConfigItem('showDownloadFolder', false),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('showDownloadFolder');
ConfigUtil.setConfigItem('showDownloadFolder', newValue);
this.showDownloadFolder();
}
});
}
}
module.exports = GeneralSection;

View File

@@ -0,0 +1,67 @@
'use strict';
const BaseComponent = require(__dirname + '/../../components/base.js');
class PreferenceNav extends BaseComponent {
constructor(props) {
super();
this.props = props;
this.navItems = ['General', 'Network', 'AddServer', 'Organizations', 'Shortcuts'];
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,128 @@
'use strict';
const {ipcRenderer} = require('electron');
const BaseSection = require(__dirname + '/base-section.js');
const ConfigUtil = require(__dirname + '/../../utils/config-util.js');
class NetworkSection extends BaseSection {
constructor(props) {
super();
this.props = props;
}
template() {
return `
<div class="settings-pane">
<div class="title">Proxy</div>
<div id="appearance-option-settings" class="settings-card">
<div class="setting-row" id="use-system-settings">
<div class="setting-description">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">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 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">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">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>Save</span>
</div>
</div>
</div>
</div>
</div>
`;
}
init() {
this.props.$root.innerHTML = this.template();
this.$proxyPAC = document.querySelector('#proxy-pac-option .setting-input-value');
this.$proxyRules = document.querySelector('#proxy-rules-option .setting-input-value');
this.$proxyBypass = document.querySelector('#proxy-bypass-option .setting-input-value');
this.$proxySaveAction = document.getElementById('proxy-save-action');
this.$manualProxyBlock = this.props.$root.querySelector('.manual-proxy-block');
this.initProxyOption();
this.$proxyPAC.value = ConfigUtil.getConfigItem('proxyPAC', '');
this.$proxyRules.value = ConfigUtil.getConfigItem('proxyRules', '');
this.$proxyBypass.value = ConfigUtil.getConfigItem('proxyBypass', '');
this.$proxySaveAction.addEventListener('click', () => {
ConfigUtil.setConfigItem('proxyPAC', this.$proxyPAC.value);
ConfigUtil.setConfigItem('proxyRules', this.$proxyRules.value);
ConfigUtil.setConfigItem('proxyBypass', this.$proxyBypass.value);
ipcRenderer.send('forward-message', 'reload-proxy', true);
});
}
initProxyOption() {
const manualProxyEnabled = ConfigUtil.getConfigItem('useManualProxy', false);
this.toggleManualProxySettings(manualProxyEnabled);
this.updateProxyOption();
}
toggleManualProxySettings(option) {
if (option) {
this.$manualProxyBlock.classList.remove('hidden');
} else {
this.$manualProxyBlock.classList.add('hidden');
}
}
updateProxyOption() {
this.generateSettingOption({
$element: document.querySelector('#use-system-settings .setting-control'),
value: ConfigUtil.getConfigItem('useSystemProxy', false),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('useSystemProxy');
const manualProxyValue = ConfigUtil.getConfigItem('useManualProxy');
if (manualProxyValue && newValue) {
ConfigUtil.setConfigItem('useManualProxy', !manualProxyValue);
this.toggleManualProxySettings(!manualProxyValue);
}
if (newValue === false) {
// Remove proxy system proxy settings
ConfigUtil.setConfigItem('proxyRules', '');
ipcRenderer.send('forward-message', 'reload-proxy', false);
}
ConfigUtil.setConfigItem('useSystemProxy', newValue);
this.updateProxyOption();
}
});
this.generateSettingOption({
$element: document.querySelector('#use-manual-settings .setting-control'),
value: ConfigUtil.getConfigItem('useManualProxy', false),
clickHandler: () => {
const newValue = !ConfigUtil.getConfigItem('useManualProxy');
const systemProxyValue = ConfigUtil.getConfigItem('useSystemProxy');
this.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);
this.updateProxyOption();
}
});
}
}
module.exports = NetworkSection;

View File

@@ -0,0 +1,89 @@
'use strict';
const BaseComponent = require(__dirname + '/../../components/base.js');
const DomainUtil = require(__dirname + '/../../utils/domain-util.js');
const shell = require('electron').shell;
class NewServerForm extends BaseComponent {
constructor(props) {
super();
this.props = props;
}
template() {
return `
<div class="server-input-container">
<div class="title">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">
<div class="server-save-action">
<button id="connect">Connect</button>
</div>
</div>
<div class="server-center">
<div class="divider">
<hr class="left"/>OR<hr class="right" />
</div>
</div>
<div class="server-center">
<div class="server-save-action">
<button id="open-create-org-link">Create a new organization</button>
</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.setting-input-value')[0];
}
submitFormHandler() {
this.$saveServerButton.children[0].innerHTML = 'Connecting...';
DomainUtil.checkDomain(this.$newServerUrl.value).then(serverConf => {
DomainUtil.addDomain(serverConf).then(() => {
this.props.onChange(this.props.index);
});
}, errorMessage => {
this.$saveServerButton.children[0].innerHTML = 'Connect';
alert(errorMessage);
});
}
openCreateNewOrgExternalLink() {
const link = 'https://zulipchat.com/new/';
const externalCreateNewOrgEl = document.getElementById('open-create-org-link');
externalCreateNewOrgEl.addEventListener('click', () => {
shell.openExternal(link);
});
}
initActions() {
this.$saveServerButton.addEventListener('click', () => {
this.submitFormHandler();
});
this.$newServerUrl.addEventListener('keypress', event => {
const EnterkeyCode = event.keyCode;
// Submit form when Enter key is pressed
if (EnterkeyCode === 13) {
this.submitFormHandler();
}
});
// open create new org link in default browser
this.openCreateNewOrgExternalLink();
}
}
module.exports = NewServerForm;

View File

@@ -0,0 +1,115 @@
'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');
const NetworkSection = require(__dirname + '/js/pages/preference/network-section.js');
const ConnectedOrgSection = require(__dirname + '/js/pages/preference/connected-org-section.js');
const ShortcutsSection = require(__dirname + '/js/pages/preference/shortcuts-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 'AddServer': {
this.section = new ServersSection({
$root: this.$settingsContainer
});
break;
}
case 'General': {
this.section = new GeneralSection({
$root: this.$settingsContainer
});
break;
}
case 'Organizations': {
this.section = new ConnectedOrgSection({
$root: this.$settingsContainer
});
break;
}
case 'Network': {
this.section = new NetworkSection({
$root: this.$settingsContainer
});
break;
}
case 'Shortcuts': {
this.section = new ShortcutsSection({
$root: this.$settingsContainer
});
break;
}
default: break;
}
this.section.init();
window.location.hash = `#${navItem}`;
}
// Handle toggling and reflect changes in preference page
handleToggle(elementName, state) {
const inputSelector = `#${elementName} .action .switch input`;
const input = document.querySelector(inputSelector);
if (input) {
input.checked = state;
}
}
registerIpcs() {
ipcRenderer.on('switch-settings-nav', (event, navItem) => {
this.handleNavigation(navItem);
});
ipcRenderer.on('toggle-sidebar-setting', (event, state) => {
this.handleToggle('sidebar-option', state);
});
ipcRenderer.on('toggletray', (event, state) => {
this.handleToggle('tray-option', state);
});
ipcRenderer.on('toggle-dnd', (event, state, newSettings) => {
this.handleToggle('show-notification-option', newSettings.showNotification);
this.handleToggle('silent-option', newSettings.silent);
if (process.platform === 'win32') {
this.handleToggle('flash-taskbar-option', newSettings.flashTaskbarOnMessage);
}
});
}
}
window.onload = () => {
const preferenceView = new PreferenceView();
preferenceView.init();
};

View File

@@ -0,0 +1,82 @@
'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 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>
<div class="server-info-right">
<div class="server-info-row server-url">
<span class="server-url-info" title="${this.props.server.url}">${this.props.server.url}</span>
</div>
<div class="server-info-row">
<div class="action red server-delete-action">
<span>Disconnect</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.$serverIcon = this.$serverInfoForm.getElementsByClassName('server-info-icon')[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 disconnect this organization?'
}, 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);
});
this.$serverIcon.addEventListener('click', () => {
ipcRenderer.send('forward-message', 'switch-server-tab', this.props.index);
});
}
}
module.exports = ServerInfoForm;

View File

@@ -0,0 +1,46 @@
'use strict';
const BaseSection = require(__dirname + '/base-section.js');
const NewServerForm = require(__dirname + '/new-server-form.js');
class ServersSection extends BaseSection {
constructor(props) {
super();
this.props = props;
}
template() {
return `
<div class="add-server-modal">
<div class="modal-container">
<div class="settings-pane" id="server-settings-pane">
<div class="page-title">Add a Zulip organization</div>
<div id="new-server-container"></div>
</div>
</div>
</div>
`;
}
init() {
this.initServers();
}
initServers() {
this.props.$root.innerHTML = '';
this.props.$root.innerHTML = this.template();
this.$newServerContainer = document.getElementById('new-server-container');
this.initNewServerForm();
}
initNewServerForm() {
new NewServerForm({
$root: this.$newServerContainer,
onChange: this.reloadApp
}).init();
}
}
module.exports = ServersSection;

View File

@@ -0,0 +1,322 @@
'use strict';
const BaseSection = require(__dirname + '/base-section.js');
const shell = require('electron').shell;
class ShortcutsSection extends BaseSection {
constructor(props) {
super();
this.props = props;
}
templateMac() {
const userOSKey = '⌘';
return `
<div class="settings-pane">
<div class="settings-card tip"><p><b><i class="material-icons md-14">settings</i>Tip: </b>These desktop app shortcuts extend the Zulip webapp's <span id="open-hotkeys-link"> keyboard shortcuts</span>.</p></div>
<div class="title">Application Shortcuts</div>
<div class="settings-card">
<table>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>,</kbd></td>
<td>Settings</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>K</kbd></td>
<td>Keyboard Shortcuts</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>Shift</kbd> + <kbd>M</kbd></td>
<td>Toggle Do Not Disturb</td>
</tr>
<tr>
<td><kbd>Shift</kbd><kbd>${userOSKey}</kbd><kbd>D</kbd></td>
<td>Reset App Settings</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>L</kbd></td>
<td>Log Out</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>H</kbd></td>
<td>Hide Zulip</td>
</tr>
<tr>
<td><kbd>Option</kbd><kbd>${userOSKey}</kbd><kbd>H</kbd></td>
<td>Hide Others</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>Q</kbd></td>
<td>Quit Zulip</td>
</tr>
</table>
<div class="setting-control"></div>
</div>
<div class="title">Edit Shortcuts</div>
<div class="settings-card">
<table>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>Z</kbd></td>
<td>Undo</td>
</tr>
<tr>
<td><kbd>Shift</kbd><kbd>${userOSKey}</kbd><kbd>Z</kbd></td>
<td>Redo</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>X</kbd></td>
<td>Cut</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>C</kbd></td>
<td>Copy</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>V</kbd></td>
<td>Paste</td>
</tr>
<tr>
<td><kbd>Shift</kbd><kbd>${userOSKey}</kbd><kbd>V</kbd></td>
<td>Paste and Match Style</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>A</kbd></td>
<td>Select All</td>
</tr>
<tr>
<td><kbd>Control</kbd><kbd>${userOSKey}</kbd><kbd>Space</kbd></td>
<td>Emoji & Symbols</td>
</tr>
</table>
<div class="setting-control"></div>
</div>
<div class="title">View Shortcuts</div>
<div class="settings-card">
<table>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>R</kbd></td>
<td>Reload</td>
</tr>
<tr>
<td><kbd>Shift</kbd><kbd>${userOSKey}</kbd><kbd>R</kbd></td>
<td>Hard Reload</td>
</tr>
<tr>
<td><kbd>Control</kbd><kbd>${userOSKey}</kbd><kbd>F</kbd></td>
<td>Enter Full Screen</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>+</kbd></td>
<td>Zoom In</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>-</kbd></td>
<td>Zoom Out</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>0</kbd></td>
<td>Actual Size</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>Shift</kbd> + <kbd>S</kbd></td>
<td>Toggle Sidebar</td>
</tr>
<tr>
<td><kbd>Option</kbd><kbd>${userOSKey}</kbd><kbd>I</kbd></td>
<td>Toggle DevTools for Zulip App</td>
</tr>
<tr>
<td><kbd>Option</kbd><kbd>${userOSKey}</kbd><kbd>U</kbd></td>
<td>Toggle DevTools for Active Tab</td>
</tr>
</table>
<div class="setting-control"></div>
</div>
<div class="title">History Shortcuts</div>
<div class="settings-card">
<table>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>←</kbd></td>
<td>Back</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>→</kbd></td>
<td>Forward</td>
</tr>
</table>
<div class="setting-control"></div>
</div>
<div class="title">Window Shortcuts</div>
<div class="settings-card">
<table>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>M</kbd></td>
<td>Minimize</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>W</kbd></td>
<td>Close</td>
</tr>
</table>
<div class="setting-control"></div>
</div>
</div>
`;
}
templateWinLin() {
const userOSKey = 'Ctrl';
return `
<div class="settings-pane">
<div class="title">Application Shortcuts</div>
<div class="settings-card">
<table>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>,</kbd></td>
<td>Settings</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>K</kbd></td>
<td>Keyboard Shortcuts</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>Shift</kbd> + <kbd>M</kbd></td>
<td>Toggle Do Not Disturb</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>L</kbd></td>
<td>Log Out</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>Q</kbd></td>
<td>Quit Zulip</td>
</tr>
</table>
<div class="setting-control"></div>
</div>
<div class="title">Edit Shortcuts</div>
<div class="settings-card">
<table>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>Z</kbd></td>
<td>Undo</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>Y</kbd></td>
<td>Redo</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>X</kbd></td>
<td>Cut</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>C</kbd></td>
<td>Copy</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>V</kbd></td>
<td>Paste</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>Shift</kbd> + <kbd>V</kbd></td>
<td>Paste and Match Style</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>A</kbd></td>
<td>Select All</td>
</tr>
</table>
<div class="setting-control"></div>
</div>
<div class="title">View Shortcuts</div>
<div class="settings-card">
<table>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>R</kbd></td>
<td>Reload</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>Shift</kbd> + <kbd>R</kbd></td>
<td>Hard Reload</td>
</tr>
<tr>
<td><kbd>F11</kbd></td>
<td>Toggle Full Screen</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>=</kbd></td>
<td>Zoom In</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>-</kbd></td>
<td>Zoom Out</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>0</kbd></td>
<td>Actual Size</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>Shift</kbd> + <kbd>S</kbd></td>
<td>Toggle Sidebar</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>Shift</kbd> + <kbd>I</kbd></td>
<td>Toggle DevTools for Zulip App</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>Shift</kbd> + <kbd>U</kbd></td>
<td>Toggle DevTools for Active Tab</td>
</tr>
</table>
<div class="setting-control"></div>
</div>
<div class="title">History Shortcuts</div>
<div class="settings-card">
<table>
<tr>
<td><kbd>Alt</kbd> + <kbd>←</kbd></td>
<td>Back</td>
</tr>
<tr>
<td><kbd>Alt</kbd> + <kbd>→</kbd></td>
<td>Forward</td>
</tr>
</table>
<div class="setting-control"></div>
</div>
<div class="title">Window Shortcuts</div>
<div class="settings-card">
<table>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>M</kbd></td>
<td>Minimize</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd> + <kbd>W</kbd></td>
<td>Close</td>
</tr>
</table>
<div class="setting-control"></div>
</div>
<div class="tip"><b><i class="material-icons md-14">lightbulb_outline</i>Tip: </b>These desktop app shortcuts extend the Zulip webapp's <span id="open-hotkeys-link">keyboard shortcuts</span>.</div>
</div>
`;
}
openHotkeysExternalLink() {
const link = 'https://zulipchat.com/help/keyboard-shortcuts';
const externalCreateNewOrgEl = document.getElementById('open-hotkeys-link');
externalCreateNewOrgEl.addEventListener('click', () => {
shell.openExternal(link);
});
}
init() {
this.props.$root.innerHTML = (process.platform === 'darwin') ?
this.templateMac() : this.templateWinLin();
this.openHotkeysExternalLink();
}
}
module.exports = ShortcutsSection;

View File

@@ -1,45 +0,0 @@
'use strict';
const {remote} = require('electron');
const prefWindow = remote.getCurrentWindow();
document.getElementById('close-button').addEventListener('click', () => {
prefWindow.close();
});
document.addEventListener('keydown', event => {
if (event.key === 'Escape' || event.keyCode === 27) {
prefWindow.close();
}
});
// eslint-disable-next-line no-unused-vars
function addDomain() {
const request = require('request');
const ipcRenderer = require('electron').ipcRenderer;
const JsonDB = require('node-json-db');
const {app} = require('electron').remote;
const db = new JsonDB(app.getPath('userData') + '/domain.json', true, true);
document.getElementById('main').innerHTML = 'checking...';
document.getElementById('pic').style.display = 'block';
let newDomain = document.getElementById('url').value;
newDomain = newDomain.replace(/^https?:\/\//, '');
const domain = 'https://' + newDomain;
const checkDomain = domain + '/static/audio/zulip.ogg';
request(checkDomain, (error, response) => {
if (!error && response.statusCode !== 404) {
document.getElementById('pic').style.display = 'none';
document.getElementById('main').innerHTML = 'Switch';
document.getElementById('urladded').innerHTML = 'Switched to ' + newDomain;
db.push('/domain', domain);
ipcRenderer.send('new-domain', domain);
} else {
document.getElementById('pic').style.display = 'none';
document.getElementById('main').innerHTML = 'Switch';
document.getElementById('urladded').innerHTML = 'Not a vaild Zulip Server.';
}
});
}

View File

@@ -0,0 +1,98 @@
'use strict';
const { ipcRenderer, shell } = require('electron');
const SetupSpellChecker = require('./spellchecker');
const ConfigUtil = require(__dirname + '/utils/config-util.js');
const LinkUtil = require(__dirname + '/utils/link-util.js');
const params = require(__dirname + '/utils/params-util.js');
// eslint-disable-next-line import/no-unassigned-import
require('./notification');
// Prevent drag and drop event in main process which prevents remote code executaion
require(__dirname + '/shared/preventdrag.js');
// eslint-disable-next-line camelcase
window.electron_bridge = require('./electron-bridge');
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', () => {
if (params.isPageParams()) {
// Get the default language of the server
const serverLanguage = page_params.default_language; // eslint-disable-line no-undef, camelcase
if (serverLanguage) {
// Set spellcheker language
ConfigUtil.setConfigItem('spellcheckerLanguage', serverLanguage);
// Init spellchecker
SetupSpellChecker.init();
}
// redirect users to network troubleshooting page
const getRestartButton = document.querySelector('.restart_get_events_button');
if (getRestartButton) {
getRestartButton.addEventListener('click', () => {
ipcRenderer.send('forward-message', 'reload-viewer');
});
}
// Open image attachment link in the lightbox instead of opening in the default browser
const { $, lightbox } = window;
$('#main_div').on('click', '.message_content p a', function (e) {
const url = $(this).attr('href');
if (LinkUtil.isImage(url)) {
const $img = $(this).parent().siblings('.message_inline_image').find('img');
// prevent the image link from opening in a new page.
e.preventDefault();
// prevent the message compose dialog from happening.
e.stopPropagation();
// Open image in the default browser if image preview is unavailable
if (!$img[0]) {
shell.openExternal(window.location.origin + url);
}
// Open image in lightbox
lightbox.open($img);
}
});
}
});
// Clean up spellchecker events after you navigate away from this page;
// otherwise, you may experience errors
window.addEventListener('beforeunload', () => {
SetupSpellChecker.unsubscribeSpellChecker();
});
// electron's globalShortcut can cause unexpected results
// so adding the reload shortcut in the old-school way
document.addEventListener('keydown', event => {
if (event.code === 'F5') {
ipcRenderer.send('forward-message', 'hard-reload');
}
});

View File

@@ -0,0 +1,17 @@
'use strict';
// This is a security fix. Following function prevents drag and drop event in the app
// so that attackers can't execute any remote code within the app
// It doesn't affect the compose box so that users can still
// use drag and drop event to share files etc
const preventDragAndDrop = () => {
const preventEvents = ['dragover', 'drop'];
preventEvents.forEach(dragEvents => {
document.addEventListener(dragEvents, event => {
event.preventDefault();
});
});
};
preventDragAndDrop();

View File

@@ -0,0 +1,62 @@
'use strict';
const { SpellCheckHandler, ContextMenuListener, ContextMenuBuilder } = require('electron-spellchecker');
const ConfigUtil = require(__dirname + '/utils/config-util.js');
const Logger = require(__dirname + '/utils/logger-util.js');
const logger = new Logger({
file: 'errors.log',
timestamp: true
});
class SetupSpellChecker {
init() {
if (ConfigUtil.getConfigItem('enableSpellchecker')) {
this.enableSpellChecker();
}
this.enableContextMenu();
}
enableSpellChecker() {
try {
this.SpellCheckHandler = new SpellCheckHandler();
} catch (err) {
logger.error(err);
}
}
enableContextMenu() {
if (this.SpellCheckHandler) {
this.SpellCheckHandler.attachToInput();
const userLanguage = ConfigUtil.getConfigItem('spellcheckerLanguage');
// eslint-disable-next-line no-unused-expressions
process.platform === 'darwin' ?
// On macOS, spellchecker fails to auto-detect the lanugage user is typing in
// that's why we need to mention it explicitly
this.SpellCheckHandler.switchLanguage(userLanguage) :
// On Linux and Windows, spellchecker can automatically detects the language the user is typing in
// and silently switches on the fly; thus we can start off as US English
this.SpellCheckHandler.switchLanguage('en-US');
}
const contextMenuBuilder = new ContextMenuBuilder(this.SpellCheckHandler);
this.contextMenuListener = new ContextMenuListener(info => {
contextMenuBuilder.showPopupMenu(info);
});
}
unsubscribeSpellChecker() {
// eslint-disable-next-line no-undef
if (this.SpellCheckHandler) {
this.SpellCheckHandler.unsubscribe();
}
if (this.contextMenuListener) {
this.contextMenuListener.unsubscribe();
}
}
}
module.exports = new SetupSpellChecker();

215
app/renderer/js/tray.js Normal file
View File

@@ -0,0 +1,215 @@
'use strict';
const path = require('path');
const electron = require('electron');
const { ipcRenderer, remote } = electron;
const { Tray, Menu, nativeImage, BrowserWindow } = remote;
const APP_ICON = path.join(__dirname, '../../resources/tray', 'tray');
const ConfigUtil = require(__dirname + '/utils/config-util.js');
const iconPath = () => {
if (process.platform === 'linux') {
return APP_ICON + 'linux.png';
}
return APP_ICON + (process.platform === 'win32' ? 'win.ico' : 'osx.png');
};
let unread = 0;
const trayIconSize = () => {
switch (process.platform) {
case 'darwin':
return 20;
case 'win32':
return 100;
case 'linux':
return 100;
default: return 80;
}
};
// Default config for Icon we might make it OS specific if needed like the size
const config = {
pixelRatio: window.devicePixelRatio,
unreadCount: 0,
showUnreadCount: true,
unreadColor: '#000000',
readColor: '#000000',
unreadBackgroundColor: '#B9FEEA',
readBackgroundColor: '#B9FEEA',
size: trayIconSize(),
thick: process.platform === 'win32'
};
const renderCanvas = function (arg) {
config.unreadCount = arg;
return new Promise(resolve => {
const SIZE = config.size * config.pixelRatio;
const PADDING = SIZE * 0.05;
const CENTER = SIZE / 2;
const HAS_COUNT = config.showUnreadCount && config.unreadCount;
const color = config.unreadCount ? config.unreadColor : config.readColor;
const backgroundColor = config.unreadCount ? config.unreadBackgroundColor : config.readBackgroundColor;
const canvas = document.createElement('canvas');
canvas.width = SIZE;
canvas.height = SIZE;
const ctx = canvas.getContext('2d');
// Circle
// If (!config.thick || config.thick && HAS_COUNT) {
ctx.beginPath();
ctx.arc(CENTER, CENTER, (SIZE / 2) - PADDING, 0, 2 * Math.PI, false);
ctx.fillStyle = backgroundColor;
ctx.fill();
ctx.lineWidth = SIZE / (config.thick ? 10 : 20);
ctx.strokeStyle = backgroundColor;
ctx.stroke();
// Count or Icon
if (HAS_COUNT) {
ctx.fillStyle = color;
ctx.textAlign = 'center';
if (config.unreadCount > 99) {
ctx.font = `${config.thick ? 'bold ' : ''}${SIZE * 0.4}px Helvetica`;
ctx.fillText('99+', CENTER, CENTER + (SIZE * 0.15));
} else if (config.unreadCount < 10) {
ctx.font = `${config.thick ? 'bold ' : ''}${SIZE * 0.5}px Helvetica`;
ctx.fillText(config.unreadCount, CENTER, CENTER + (SIZE * 0.20));
} else {
ctx.font = `${config.thick ? 'bold ' : ''}${SIZE * 0.5}px Helvetica`;
ctx.fillText(config.unreadCount, CENTER, CENTER + (SIZE * 0.15));
}
resolve(canvas);
}
});
};
/**
* Renders the tray icon as a native image
* @param arg: Unread count
* @return the native image
*/
const renderNativeImage = function (arg) {
return Promise.resolve()
.then(() => renderCanvas(arg))
.then(canvas => {
const pngData = nativeImage.createFromDataURL(canvas.toDataURL('image/png')).toPNG();
return Promise.resolve(nativeImage.createFromBuffer(pngData, config.pixelRatio));
});
};
function sendAction(action) {
const win = BrowserWindow.getAllWindows()[0];
if (process.platform === 'darwin') {
win.restore();
}
win.webContents.send(action);
}
const createTray = function () {
window.tray = new Tray(iconPath());
const contextMenu = Menu.buildFromTemplate([
{
label: 'Zulip',
click() {
ipcRenderer.send('focus-app');
}
},
{
label: 'Settings',
click() {
ipcRenderer.send('focus-app');
sendAction('open-settings');
}
},
{
type: 'separator'
},
{
label: 'Quit',
click() {
ipcRenderer.send('quit-app');
}
}
]);
window.tray.setContextMenu(contextMenu);
if (process.platform === 'linux' || process.platform === 'win32') {
window.tray.on('click', () => {
ipcRenderer.send('toggle-app');
});
}
};
ipcRenderer.on('destroytray', event => {
if (!window.tray) {
return;
}
window.tray.destroy();
if (window.tray.isDestroyed()) {
window.tray = null;
} else {
throw new Error('Tray icon not properly destroyed.');
}
return event;
});
ipcRenderer.on('tray', (event, arg) => {
if (!window.tray) {
return;
}
// We don't want to create tray from unread messages on macOS since it already has dock badges.
if (process.platform === 'linux' || process.platform === 'win32') {
if (arg === 0) {
unread = arg;
window.tray.setImage(iconPath());
window.tray.setToolTip('No unread messages');
} else {
unread = arg;
renderNativeImage(arg).then(image => {
window.tray.setImage(image);
window.tray.setToolTip(arg + ' unread messages');
});
}
}
});
function toggleTray() {
let state;
if (window.tray) {
state = false;
window.tray.destroy();
if (window.tray.isDestroyed()) {
window.tray = null;
}
ConfigUtil.setConfigItem('trayIcon', false);
} else {
state = true;
createTray();
if (process.platform === 'linux' || process.platform === 'win32') {
renderNativeImage(unread).then(image => {
window.tray.setImage(image);
window.tray.setToolTip(unread + ' unread messages');
});
}
ConfigUtil.setConfigItem('trayIcon', true);
}
const selector = 'webview:not([class*=disabled])';
const webview = document.querySelector(selector);
const webContents = webview.getWebContents();
webContents.send('toggletray', state);
}
ipcRenderer.on('toggletray', toggleTray);
if (ConfigUtil.getConfigItem('trayIcon', true)) {
createTray();
}

View File

@@ -0,0 +1,86 @@
'use strict';
const { app, dialog } = require('electron').remote;
const fs = require('fs');
const path = require('path');
const JsonDB = require('node-json-db');
const Logger = require('./logger-util');
const { initSetUp } = require('./default-util');
initSetUp();
const logger = new Logger({
file: `certificate-util.log`,
timestamp: true
});
let instance = null;
const certificatesDir = `${app.getPath('userData')}/certificates`;
class CertificateUtil {
constructor() {
if (instance) {
return instance;
} else {
instance = this;
}
this.reloadDB();
return instance;
}
getCertificate(server, defaultValue = null) {
this.reloadDB();
const value = this.db.getData('/')[server];
if (value === undefined) {
return defaultValue;
} else {
return value;
}
}
// Function to copy the certificate to userData folder
copyCertificate(server, location, fileName) {
let copied = false;
const filePath = `${certificatesDir}/${fileName}`;
try {
fs.copyFileSync(location, filePath);
copied = true;
} catch (err) {
dialog.showErrorBox(
'Error saving certificate',
'We encountered error while saving the certificate.'
);
logger.error('Error while copying the certificate to certificates folder.');
logger.error(err);
}
return copied;
}
setCertificate(server, fileName) {
const filePath = `${certificatesDir}/${fileName}`;
this.db.push(`/${server}`, filePath, true);
this.reloadDB();
}
removeCertificate(server) {
this.db.delete(`/${server}`);
this.reloadDB();
}
reloadDB() {
const settingsJsonPath = path.join(app.getPath('userData'), '/config/certificates.json');
try {
const file = fs.readFileSync(settingsJsonPath, 'utf8');
JSON.parse(file);
} catch (err) {
if (fs.existsSync(settingsJsonPath)) {
fs.unlinkSync(settingsJsonPath);
dialog.showErrorBox(
'Error saving settings',
'We encountered error while saving the certificate.'
);
logger.error('Error while JSON parsing certificates.json: ');
logger.error(err);
}
}
this.db = new JsonDB(settingsJsonPath, true, true);
}
}
module.exports = new CertificateUtil();

View File

@@ -0,0 +1,25 @@
'use strict';
let instance = null;
class CommonUtil {
constructor() {
if (instance) {
return instance;
} else {
instance = this;
}
return instance;
}
// unescape already encoded/escaped strings
decodeString(string) {
const parser = new DOMParser();
const dom = parser.parseFromString(
'<!doctype html><body>' + string,
'text/html');
return dom.body.textContent;
}
}
module.exports = new CommonUtil();

View File

@@ -0,0 +1,99 @@
'use strict';
const fs = require('fs');
const path = require('path');
const process = require('process');
const JsonDB = require('node-json-db');
const Logger = require('./logger-util');
const logger = new Logger({
file: 'config-util.log',
timestamp: true
});
let instance = null;
let dialog = null;
let app = null;
/* To make the util runnable in both main and renderer process */
if (process.type === 'renderer') {
const remote = require('electron').remote;
dialog = remote.dialog;
app = remote.app;
} else {
const electron = require('electron');
dialog = electron.dialog;
app = electron.app;
}
class ConfigUtil {
constructor() {
if (instance) {
return instance;
} else {
instance = this;
}
this.reloadDB();
return instance;
}
getConfigItem(key, defaultValue = null) {
try {
this.db.reload();
} catch (err) {
logger.error('Error while reloading settings.json: ');
logger.error(err);
}
const value = this.db.getData('/')[key];
if (value === undefined) {
this.setConfigItem(key, defaultValue);
return defaultValue;
} else {
return value;
}
}
// This function returns whether a key exists in the configuration file (settings.json)
isConfigItemExists(key) {
try {
this.db.reload();
} catch (err) {
logger.error('Error while reloading settings.json: ');
logger.error(err);
}
const value = this.db.getData('/')[key];
return (value !== undefined);
}
setConfigItem(key, value) {
this.db.push(`/${key}`, value, true);
this.db.save();
}
removeConfigItem(key) {
this.db.delete(`/${key}`);
this.db.save();
}
reloadDB() {
const settingsJsonPath = path.join(app.getPath('userData'), '/config/settings.json');
try {
const file = fs.readFileSync(settingsJsonPath, 'utf8');
JSON.parse(file);
} catch (err) {
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(err);
logger.reportSentry(err);
}
}
this.db = new JsonDB(settingsJsonPath, true, true);
}
}
module.exports = new ConfigUtil();

View File

@@ -0,0 +1,78 @@
const fs = require('fs');
let app = null;
let setupCompleted = false;
if (process.type === 'renderer') {
app = require('electron').remote.app;
} else {
app = require('electron').app;
}
const zulipDir = app.getPath('userData');
const logDir = `${zulipDir}/Logs/`;
const certificatesDir = `${zulipDir}/certificates/`;
const configDir = `${zulipDir}/config/`;
const initSetUp = () => {
// 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);
}
if (!fs.existsSync(certificatesDir)) {
fs.mkdirSync(certificatesDir);
}
// 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 certificatesJson = `${zulipDir}/certificates.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: certificatesJson,
fileName: `certificates.json`
},
{
path: settingsJson,
fileName: `settings.json`
},
{
path: updatesJson,
fileName: `updates.json`
}
];
configData.forEach(data => {
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;
}
};
module.exports = {
initSetUp
};

View File

@@ -0,0 +1,41 @@
'use strict';
const ConfigUtil = require(__dirname + '/config-util.js');
function toggle() {
const dnd = !ConfigUtil.getConfigItem('dnd', false);
const dndSettingList = ['showNotification', 'silent'];
if (process.platform === 'win32') {
dndSettingList.push('flashTaskbarOnMessage');
}
let newSettings;
if (dnd) {
const oldSettings = {};
newSettings = {};
// Iterate through the dndSettingList.
for (const settingName of dndSettingList) {
// Store the current value of setting.
oldSettings[settingName] = ConfigUtil.getConfigItem(settingName);
// New value of setting.
newSettings[settingName] = (settingName === 'silent');
}
// Store old value in oldSettings.
ConfigUtil.setConfigItem('dndPreviousSettings', oldSettings);
} else {
newSettings = ConfigUtil.getConfigItem('dndPreviousSettings');
}
for (const settingName of dndSettingList) {
ConfigUtil.setConfigItem(settingName, newSettings[settingName]);
}
ConfigUtil.setConfigItem('dnd', dnd);
return {dnd, newSettings};
}
module.exports = {
toggle
};

View File

@@ -0,0 +1,328 @@
'use strict';
const { app, dialog } = require('electron').remote;
const fs = require('fs');
const path = require('path');
const JsonDB = require('node-json-db');
const request = require('request');
const escape = require('escape-html');
const Logger = require('./logger-util');
const CertificateUtil = require(__dirname + '/certificate-util.js');
const ProxyUtil = require(__dirname + '/proxy-util.js');
const ConfigUtil = require(__dirname + '/config-util.js');
const logger = new Logger({
file: `domain-util.log`,
timestamp: true
});
let instance = null;
const defaultIconUrl = '../renderer/img/icon.png';
class DomainUtil {
constructor() {
if (instance) {
return instance;
} else {
instance = this;
}
this.reloadDB();
// Migrate from old schema
if (this.db.getData('/').domain) {
this.addDomain({
alias: 'Zulip',
url: this.db.getData('/domain')
});
this.db.delete('/domain');
}
return instance;
}
getDomains() {
this.reloadDB();
if (this.db.getData('/').domains === undefined) {
return [];
} else {
return this.db.getData('/domains');
}
}
getDomain(index) {
this.reloadDB();
return this.db.getData(`/domains[${index}]`);
}
updateDomain(index, server) {
this.reloadDB();
this.db.push(`/domains[${index}]`, server, true);
}
addDomain(server) {
return new Promise(resolve => {
if (server.icon) {
this.saveServerIcon(server.icon).then(localIconUrl => {
server.icon = localIconUrl;
this.db.push('/domains[]', server, true);
this.reloadDB();
resolve();
});
} else {
server.icon = defaultIconUrl;
this.db.push('/domains[]', server, true);
this.reloadDB();
resolve();
}
});
}
removeDomains() {
this.db.delete('/domains');
this.reloadDB();
}
removeDomain(index) {
this.db.delete(`/domains[${index}]`);
this.reloadDB();
}
// Check if domain is already added
duplicateDomain(domain) {
domain = this.formatUrl(domain);
const servers = this.getDomains();
for (const i in servers) {
if (servers[i].url === domain) {
return true;
}
}
return false;
}
checkDomain(domain, silent = false) {
if (!silent && this.duplicateDomain(domain)) {
// Do not check duplicate in silent mode
return Promise.reject('This server has been added.');
}
domain = this.formatUrl(domain);
const certificate = CertificateUtil.getCertificate(encodeURIComponent(domain));
let certificateLocation = '';
if (certificate) {
// To handle case where certificate has been moved from the location in certificates.json
try {
certificateLocation = fs.readFileSync(certificate);
} catch (err) {
logger.warn('Error while trying to get certificate: ' + err);
}
}
const proxyEnabled = ConfigUtil.getConfigItem('useManualProxy') || ConfigUtil.getConfigItem('useSystemProxy');
// If certificate for the domain exists add it as a ca key in the request's parameter else consider only domain as the parameter for request
// Add proxy as a parameter if it sbeing used.
const checkDomain = {
url: domain + '/static/audio/zulip.ogg',
ca: (certificateLocation) ? certificateLocation : '',
proxy: proxyEnabled ? ProxyUtil.getProxy(domain) : ''
};
const serverConf = {
icon: defaultIconUrl,
url: domain,
alias: domain
};
return new Promise((resolve, reject) => {
request(checkDomain, (error, response) => {
// If the domain contains following strings we just bypass the server
const whitelistDomains = [
'zulipdev.org'
];
// make sure that error is an error or string not undefined
// so validation does not throw error.
error = error || '';
const certsError = error.toString().includes('certificate');
if (!error && response.statusCode < 400) {
// Correct
this.getServerSettings(domain).then(serverSettings => {
resolve(serverSettings);
}, () => {
resolve(serverConf);
});
} else if (domain.indexOf(whitelistDomains) >= 0 || certsError) {
if (silent) {
this.getServerSettings(domain).then(serverSettings => {
resolve(serverSettings);
}, () => {
resolve(serverConf);
});
} else {
// Report error to sentry to get idea of possible certificate errors
// users get when adding the servers
logger.reportSentry(new Error(error));
const certErrorMessage = `Do you trust certificate from ${domain}? \n ${error}`;
const certErrorDetail = `The organization you're connecting to is either someone impersonating the Zulip server you entered, or the server you're trying to connect to is configured in an insecure way.
\nIf you have a valid certificate please add it from Settings>Organizations and try to add the organization again.
\nUnless you have a good reason to believe otherwise, you should not proceed.
\nYou can click here if you'd like to proceed with the connection.`;
dialog.showMessageBox({
type: 'warning',
buttons: ['Yes', 'No'],
defaultId: 1,
message: certErrorMessage,
detail: certErrorDetail
}, response => {
if (response === 0) {
this.getServerSettings(domain).then(serverSettings => {
resolve(serverSettings);
}, () => {
resolve(serverConf);
});
} else {
reject('Untrusted Certificate.');
}
});
}
} else {
const invalidZulipServerError = `${domain} does not appear to be a valid Zulip server. Make sure that \
\n (1) you can connect to that URL in a web browser and \n (2) if you need a proxy to connect to the Internet, that you've configured your proxy in the Network settings \n (3) its a zulip server \
\n (4) the server has a valid certificate, you can add custom certificates in Settings>Organizations`;
reject(invalidZulipServerError);
}
});
});
}
getServerSettings(domain) {
const proxyEnabled = ConfigUtil.getConfigItem('useManualProxy') || ConfigUtil.getConfigItem('useSystemProxy');
const serverSettingsOptions = {
url: domain + '/api/v1/server_settings',
proxy: proxyEnabled ? ProxyUtil.getProxy(domain) : ''
};
return new Promise((resolve, reject) => {
request(serverSettingsOptions, (error, response) => {
if (!error && response.statusCode === 200) {
const data = JSON.parse(response.body);
if (data.hasOwnProperty('realm_icon') && data.realm_icon) {
resolve({
// Some Zulip Servers use absolute URL for server icon whereas others use relative URL
// Following check handles both the cases
icon: data.realm_icon.startsWith('/') ? data.realm_uri + data.realm_icon : data.realm_icon,
url: data.realm_uri,
alias: escape(data.realm_name)
});
}
} else {
reject('Zulip server version < 1.6.');
}
});
});
}
saveServerIcon(url) {
const proxyEnabled = ConfigUtil.getConfigItem('useManualProxy') || ConfigUtil.getConfigItem('useSystemProxy');
const serverIconOptions = {
url,
proxy: proxyEnabled ? ProxyUtil.getProxy(url) : ''
};
// The save will always succeed. If url is invalid, downgrade to default icon.
return new Promise(resolve => {
const filePath = this.generateFilePath(url);
const file = fs.createWriteStream(filePath);
try {
request(serverIconOptions).on('response', response => {
response.on('error', err => {
logger.log('Could not get server icon.');
logger.log(err);
logger.reportSentry(err);
resolve(defaultIconUrl);
});
response.pipe(file).on('finish', () => {
resolve(filePath);
});
}).on('error', err => {
logger.log('Could not get server icon.');
logger.log(err);
logger.reportSentry(err);
resolve(defaultIconUrl);
});
} catch (err) {
logger.log('Could not get server icon.');
logger.log(err);
logger.reportSentry(err);
resolve(defaultIconUrl);
}
});
}
updateSavedServer(url, index) {
// Does not promise successful update
this.checkDomain(url, true).then(newServerConf => {
this.saveServerIcon(newServerConf.icon).then(localIconUrl => {
newServerConf.icon = localIconUrl;
this.updateDomain(index, newServerConf);
this.reloadDB();
});
});
}
reloadDB() {
const domainJsonPath = path.join(app.getPath('userData'), 'config/domain.json');
try {
const file = fs.readFileSync(domainJsonPath, 'utf8');
JSON.parse(file);
} catch (err) {
if (fs.existsSync(domainJsonPath)) {
fs.unlinkSync(domainJsonPath);
dialog.showErrorBox(
'Error saving new organization',
'There seems to be error while saving new organization, ' +
'you may have to re-add your previous organizations back.'
);
logger.error('Error while JSON parsing domain.json: ');
logger.error(err);
logger.reportSentry(err);
}
}
this.db = new JsonDB(domainJsonPath, true, true);
}
generateFilePath(url) {
const dir = `${app.getPath('userData')}/server-icons`;
const extension = path.extname(url).split('?')[0];
let hash = 5381;
let len = url.length;
while (len) {
hash = (hash * 33) ^ url.charCodeAt(--len);
}
// Create 'server-icons' directory if not existed
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
return `${dir}/${hash >>> 0}${extension}`;
}
formatUrl(domain) {
const hasPrefix = (domain.indexOf('http') === 0);
if (hasPrefix) {
return domain;
} else {
return (domain.indexOf('localhost:') >= 0) ? `http://${domain}` : `https://${domain}`;
}
}
}
module.exports = new DomainUtil();

View File

@@ -0,0 +1,45 @@
'use strict';
const wurl = require('wurl');
let instance = null;
class LinkUtil {
constructor() {
if (instance) {
return instance;
} else {
instance = this;
}
return instance;
}
isInternal(currentUrl, newUrl) {
const currentDomain = wurl('hostname', currentUrl);
const newDomain = wurl('hostname', newUrl);
const sameDomainUrl = (currentDomain === newDomain || newUrl === currentUrl + '/');
const isUploadsUrl = newUrl.includes(currentUrl + '/user_uploads/');
const isInternalUrl = newUrl.includes('/#narrow') || isUploadsUrl;
return {
isInternalUrl: sameDomainUrl && isInternalUrl,
isUploadsUrl
};
}
isImage(url) {
// test for images extension as well as urls like .png?s=100
const isImageUrl = /\.(bmp|gif|jpg|jpeg|png|webp)\?*.*$/i;
return isImageUrl.test(url);
}
isPDF(url) {
// test for pdf extension
const isPDFUrl = /\.(pdf)\?*.*$/i;
return isPDFUrl.test(url);
}
}
module.exports = new LinkUtil();

View File

@@ -0,0 +1,74 @@
'use strict';
const fs = require('fs');
const path = require('path');
const process = require('process');
const remote =
process.type === 'renderer' ? require('electron').remote : require('electron');
const JsonDB = require('node-json-db');
const Logger = require('./logger-util');
const logger = new Logger({
file: 'linux-update-util.log',
timestamp: true
});
/* To make the util runnable in both main and renderer process */
const { dialog, app } = remote;
let instance = null;
class LinuxUpdateUtil {
constructor() {
if (instance) {
return instance;
} else {
instance = this;
}
this.reloadDB();
return instance;
}
getUpdateItem(key, defaultValue = null) {
this.reloadDB();
const value = this.db.getData('/')[key];
if (value === undefined) {
this.setUpdateItem(key, defaultValue);
return defaultValue;
} else {
return value;
}
}
setUpdateItem(key, value) {
this.db.push(`/${key}`, value, true);
this.reloadDB();
}
removeUpdateItem(key) {
this.db.delete(`/${key}`);
this.reloadDB();
}
reloadDB() {
const linuxUpdateJsonPath = path.join(app.getPath('userData'), '/config/updates.json');
try {
const file = fs.readFileSync(linuxUpdateJsonPath, 'utf8');
JSON.parse(file);
} catch (err) {
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(err);
}
}
this.db = new JsonDB(linuxUpdateJsonPath, true, true);
}
}
module.exports = new LinuxUpdateUtil();

View File

@@ -0,0 +1,93 @@
const NodeConsole = require('console').Console;
const fs = require('fs');
const isDev = require('electron-is-dev');
const { initSetUp } = require('./default-util');
const { sentryInit, captureException } = require('./sentry-util');
initSetUp();
sentryInit();
let app = null;
if (process.type === 'renderer') {
app = require('electron').remote.app;
} else {
app = require('electron').app;
}
const browserConsole = console;
const logDir = `${app.getPath('userData')}/Logs`;
class Logger {
constructor(opts = {}) {
let {
timestamp = true,
file = 'console.log',
level = true,
logInDevMode = false
} = opts;
file = `${logDir}/${file}`;
if (timestamp === true) {
timestamp = this.getTimestamp;
}
const fileStream = fs.createWriteStream(file, { flags: 'a' });
const nodeConsole = new NodeConsole(fileStream);
this.nodeConsole = nodeConsole;
this.timestamp = timestamp;
this.level = level;
this.logInDevMode = logInDevMode;
this.setUpConsole();
}
_log(type, ...args) {
const {
nodeConsole, timestamp, level, logInDevMode
} = this;
let nodeConsoleLog;
/* eslint-disable no-fallthrough */
switch (true) {
case typeof timestamp === 'function':
args.unshift(timestamp() + ' |\t');
case (level !== false):
args.unshift(type.toUpperCase() + ' |');
case isDev || logInDevMode:
nodeConsoleLog = nodeConsole[type] || nodeConsole.log;
nodeConsoleLog.apply(null, args);
default: break;
}
/* eslint-enable no-fallthrough */
browserConsole[type].apply(null, args);
}
setUpConsole() {
for (const type in browserConsole) {
this.setupConsoleMethod(type);
}
}
setupConsoleMethod(type) {
this[type] = (...args) => {
this._log(type, ...args);
};
}
getTimestamp() {
const date = new Date();
const timestamp =
`${date.getMonth()}/${date.getDate()} ` +
`${date.getMinutes()}:${date.getSeconds()}`;
return timestamp;
}
reportSentry(err) {
captureException(err);
}
}
module.exports = Logger;

View File

@@ -0,0 +1,15 @@
// This util function returns the page params if they're present else returns null
function isPageParams() {
let webpageParams = null;
try {
// eslint-disable-next-line no-undef, camelcase
webpageParams = page_params;
} catch (err) {
webpageParams = null;
}
return webpageParams;
}
module.exports = {
isPageParams
};

View File

@@ -0,0 +1,130 @@
'use strict';
const url = require('url');
const ConfigUtil = require('./config-util.js');
let instance = null;
class ProxyUtil {
constructor() {
if (instance) {
return instance;
} else {
instance = this;
}
return instance;
}
// Return proxy to be used for a particular uri, to be used for request
getProxy(uri) {
uri = url.parse(uri);
const proxyRules = ConfigUtil.getConfigItem('proxyRules', '').split(';');
// If SPS is on and system uses no proxy then request should not try to use proxy from
// environment. NO_PROXY = '*' makes request ignore all environment proxy variables.
if (proxyRules[0] === '') {
process.env.NO_PROXY = '*';
return;
}
const proxyRule = {};
if (uri.protocol === 'http:') {
proxyRules.forEach(proxy => {
if (proxy.includes('http=')) {
proxyRule.hostname = proxy.split('http=')[1].trim().split(':')[0];
proxyRule.port = proxy.split('http=')[1].trim().split(':')[1];
}
});
return proxyRule;
}
if (uri.protocol === 'https:') {
proxyRules.forEach(proxy => {
if (proxy.includes('https=')) {
proxyRule.hostname = proxy.split('https=')[1].trim().split(':')[0];
proxyRule.port = proxy.split('https=')[1].trim().split(':')[1];
}
});
return proxyRule;
}
}
resolveSystemProxy(mainWindow) {
const page = mainWindow.webContents;
const ses = page.session;
const resolveProxyUrl = 'www.google.com';
// Check HTTP Proxy
const httpProxy = new Promise(resolve => {
ses.resolveProxy('http://' + resolveProxyUrl, proxy => {
let httpString = '';
if (proxy !== 'DIRECT') {
// in case of proxy HTTPS url:port, windows gives first word as HTTPS while linux gives PROXY
// for all other HTTP or direct url:port both uses PROXY
if (proxy.includes('PROXY') || proxy.includes('HTTPS')) {
httpString = 'http=' + proxy.split('PROXY')[1] + ';';
}
}
resolve(httpString);
});
});
// Check HTTPS Proxy
const httpsProxy = new Promise(resolve => {
ses.resolveProxy('https://' + resolveProxyUrl, proxy => {
let httpsString = '';
if (proxy !== 'DIRECT' || proxy.includes('HTTPS')) {
// in case of proxy HTTPS url:port, windows gives first word as HTTPS while linux gives PROXY
// for all other HTTP or direct url:port both uses PROXY
if (proxy.includes('PROXY' || proxy.includes('HTTPS'))) {
httpsString += 'https=' + proxy.split('PROXY')[1] + ';';
}
}
resolve(httpsString);
});
});
// Check FTP Proxy
const ftpProxy = new Promise(resolve => {
ses.resolveProxy('ftp://' + resolveProxyUrl, proxy => {
let ftpString = '';
if (proxy !== 'DIRECT') {
if (proxy.includes('PROXY')) {
ftpString += 'ftp=' + proxy.split('PROXY')[1] + ';';
}
}
resolve(ftpString);
});
});
// Check SOCKS Proxy
const socksProxy = new Promise(resolve => {
ses.resolveProxy('socks4://' + resolveProxyUrl, proxy => {
let socksString = '';
if (proxy !== 'DIRECT') {
if (proxy.includes('SOCKS5')) {
socksString += 'socks=' + proxy.split('SOCKS5')[1] + ';';
} else if (proxy.includes('SOCKS4')) {
socksString += 'socks=' + proxy.split('SOCKS4')[1] + ';';
} else if (proxy.includes('PROXY')) {
socksString += 'socks=' + proxy.split('PROXY')[1] + ';';
}
}
resolve(socksString);
});
});
Promise.all([httpProxy, httpsProxy, ftpProxy, socksProxy]).then(values => {
let proxyString = '';
values.forEach(proxy => {
proxyString += proxy;
});
ConfigUtil.setConfigItem('systemProxyRules', proxyString);
const useSystemProxy = ConfigUtil.getConfigItem('useSystemProxy');
if (useSystemProxy) {
ConfigUtil.setConfigItem('proxyRules', proxyString);
}
});
}
}
module.exports = new ProxyUtil();

View File

@@ -0,0 +1,60 @@
const isOnline = require('is-online');
const Logger = require('./logger-util');
const logger = new Logger({
file: `domain-util.log`,
timestamp: true
});
class ReconnectUtil {
constructor(serverManagerView) {
this.serverManagerView = serverManagerView;
this.alreadyReloaded = false;
}
clearState() {
this.alreadyReloaded = false;
}
pollInternetAndReload() {
const pollInterval = setInterval(() => {
this._checkAndReload()
.then(status => {
if (status) {
this.alreadyReloaded = true;
clearInterval(pollInterval);
}
});
}, 1500);
}
_checkAndReload() {
return new Promise(resolve => {
if (!this.alreadyReloaded) { // eslint-disable-line no-negated-condition
isOnline()
.then(online => {
if (online) {
if (!this.alreadyReloaded) {
this.serverManagerView.reloadView();
}
logger.log('You\'re back online.');
return resolve(true);
}
logger.log('There is no internet connection, try checking network cables, modem and router.');
const errMsgHolder = document.querySelector('#description');
if (errMsgHolder) {
errMsgHolder.innerHTML = `
<div>Your internet connection doesn't seem to work properly!</div>
<div>Verify that it works and then click try again.</div>`;
}
return resolve(false);
});
} else {
return resolve(true);
}
});
}
}
module.exports = ReconnectUtil;

View File

@@ -0,0 +1,20 @@
const { init, captureException } = require('@sentry/electron');
const isDev = require('electron-is-dev');
const sentryInit = () => {
if (!isDev) {
init({
dsn: 'SENTRY_DSN',
// 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
});
}
};
module.exports = {
sentryInit,
captureException
};

View File

@@ -0,0 +1,55 @@
'use strict';
const {app} = require('electron').remote;
const os = require('os');
let instance = null;
class SystemUtil {
constructor() {
if (instance) {
return instance;
} else {
instance = this;
}
this.connectivityERR = [
'ERR_INTERNET_DISCONNECTED',
'ERR_PROXY_CONNECTION_FAILED',
'ERR_CONNECTION_RESET',
'ERR_NOT_CONNECTED',
'ERR_NAME_NOT_RESOLVED',
'ERR_NETWORK_CHANGED'
];
this.userAgent = null;
return instance;
}
getOS() {
if (os.platform() === 'darwin') {
return 'Mac';
}
if (os.platform() === 'linux') {
return 'Linux';
}
if (os.platform() === 'win32' || os.platform() === 'win64') {
if (parseFloat(os.release()) < 6.2) {
return 'Windows 7';
} else {
return 'Windows 10';
}
}
}
setUserAgent(webViewUserAgent) {
this.userAgent = 'ZulipElectron/' + app.getVersion() + ' ' + webViewUserAgent;
}
getUserAgent() {
return this.userAgent;
}
}
module.exports = new SystemUtil();

56
app/renderer/main.html Normal file
View File

@@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="en" class="responsive desktop">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width">
<title>Zulip</title>
<link rel="stylesheet" href="css/main.css" type="text/css" media="screen">
</head>
<body>
<div id="content">
<div class="popup">
<span class="popuptext hidden" id="fullscreen-popup"></span>
</div>
<div id="sidebar" class="toggle-sidebar">
<div id="view-controls-container">
<div id="tabs-container"></div>
<div id="add-tab" class="tab functional-tab">
<div class="server-tab" id="add-action">
<i class="material-icons">add</i>
</div>
<span id="add-server-tooltip" style="display:none">Add organization</span>
</div>
</div>
<div id="actions-container">
<div class="action-button" id="dnd-action">
<i class="material-icons md-48">notifications</i>
<span id="dnd-tooltip" style="display:none">Do Not Disturb</span>
</div>
<div class="action-button" id="reload-action">
<i class="material-icons md-48">refresh</i>
<span id="reload-tooltip" style="display:none">Reload</span>
</div>
<div class="action-button disable" id="back-action">
<i class="material-icons md-48">arrow_back</i>
<span id="back-tooltip" style="display:none">Go Back</span>
</div>
<div class="action-button" id="settings-action">
<i class="material-icons md-48">settings</i>
<span id="setting-tooltip" style="display:none">Settings</span>
</div>
</div>
</div>
<div id="main-container">
<div id="webviews-container"></div>
</div>
</div>
<div id="feedback-modal">
<send-feedback></send-feedback>
</div>
</body>
<script src="js/main.js"></script>
<script>require('./js/shared/preventdrag.js')</script>
</html>

22
app/renderer/network.html Normal file
View File

@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en" class="responsive desktop">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width">
<title>Zulip - Network Troubleshooting</title>
<link rel="stylesheet" href="css/network.css" type="text/css" media="screen">
</head>
<body>
<div id="content">
<div id="picture"><img src="img/zulip_network.png"></div>
<div id="title">Zulip can't connect</div>
<div id="description">
<div>Your computer seems to be offline.</div>
<div>We will keep trying to reconnect, or you can try now.</div>
</div>
<div id="reconnect">Try now</div>
</div>
</body>
<script src="js/pages/network.js"></script>
<script>require('./js/shared/preventdrag.js')</script>
</html>

View File

@@ -1,22 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="css/pref.css">
<script type="text/javascript">
</script>
</head>
<body>
<div class="close" id="close-button">Close</div>
<div class="form">
<form onsubmit="addDomain(); return false">
<input id="url" type="text" placeholder="zulip.example.com">
<button type="submit" id="main" value="Submit" onclick="addDomain();">
Switch</button>
<img id="pic" src="img/loader.gif" />
</form>
<p id="urladded"><p>
</div>
<script src="js/pref.js"></script>
</body>
</html>

View File

@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en" class="responsive desktop">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width">
<title>Zulip - Settings</title>
<link rel="stylesheet" href="css/preference.css" type="text/css" media="screen">
</head>
<body>
<div id="content">
<div id="sidebar"></div>
<div id="settings-container"></div>
</div>
</body>
<script src="js/pages/preference/preference.js"></script>
<script>require('./js/shared/preventdrag.js')</script>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 333 B

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 737 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

39
app/translations/bg.json Normal file
View File

@@ -0,0 +1,39 @@
{
"About Zulip": "За Зулип",
"Actual Size": "Реален размер",
"Back": "обратно",
"Close": "Близо",
"Copy": "копие",
"Cut": "Разрез",
"Delete": "Изтрий",
"Desktop App Settings": "Настройки за настолни приложения",
"Edit": "редактиране",
"File": "досие",
"Forward": "напред",
"Hard Reload": "Трудно презареждане",
"Help": "Помогне",
"History": "история",
"Keyboard Shortcuts": "Комбинация от клавиши",
"Log Out": "Излез от профила си",
"Minimize": "Минимизиране",
"Paste": "паста",
"Paste and Match Style": "Поставяне и съвпадение на стила",
"Quit": "напускам",
"Redo": "ремонтирам",
"Reload": "Презареди",
"Report an issue...": "Подаване на сигнал за проблем ...",
"Reset App Settings": "Нулирайте настройките на приложението",
"Select All": "Избери всички",
"Show App Logs": "Показване на регистрационните файлове на приложения",
"Toggle DevTools for Active Tab": "Превключете DevTools за активен раздел",
"Toggle DevTools for Zulip App": "Превключете DevTools за Zulip App",
"Toggle Full Screen": "Превключване на цял екран",
"Toggle Sidebar": "Превключване на страничната лента",
"Toggle Tray Icon": "Превключване на иконата на тавата",
"Undo": "премахвам",
"View": "изглед",
"Window": "прозорец",
"Zoom In": "Увеличавам",
"Zoom Out": "Отдалечавам",
"Zulip Help": "Помощ за Zulip"
}

39
app/translations/cs.json Normal file
View File

@@ -0,0 +1,39 @@
{
"About Zulip": "O společnosti Zulip",
"Actual Size": "Aktuální velikost",
"Back": "Zadní",
"Close": "Zavřít",
"Copy": "kopírovat",
"Cut": "Střih",
"Delete": "Vymazat",
"Desktop App Settings": "Nastavení aplikace Desktop",
"Edit": "Upravit",
"File": "Soubor",
"Forward": "Vpřed",
"Hard Reload": "Hard Reload",
"Help": "Pomoc",
"History": "Dějiny",
"Keyboard Shortcuts": "Klávesové zkratky",
"Log Out": "Odhlásit se",
"Minimize": "Minimalizujte",
"Paste": "Vložit",
"Paste and Match Style": "Vložit a přizpůsobit styl",
"Quit": "Přestat",
"Redo": "Předělat",
"Reload": "Znovu načíst",
"Report an issue...": "Nahlásit problém...",
"Reset App Settings": "Obnovit nastavení aplikace",
"Select All": "Vybrat vše",
"Show App Logs": "Zobrazit protokoly aplikace",
"Toggle DevTools for Active Tab": "Přepínač DevTools pro aktivní kartu",
"Toggle DevTools for Zulip App": "Přepnout nástroj DevTools pro aplikaci Zulip App",
"Toggle Full Screen": "Přepnout na celou obrazovku",
"Toggle Sidebar": "Přepnout postranní panel",
"Toggle Tray Icon": "Přepnout ikonu zásobníku",
"Undo": "vrátit",
"View": "Pohled",
"Window": "Okno",
"Zoom In": "Přiblížit",
"Zoom Out": "Oddálit",
"Zulip Help": "Zulip Nápověda"
}

39
app/translations/de.json Normal file
View File

@@ -0,0 +1,39 @@
{
"About Zulip": "Über Zulip",
"Actual Size": "Tatsächliche Größe",
"Back": "Zurück",
"Close": "Schließen",
"Copy": "Kopieren",
"Cut": "Schnitt",
"Delete": "Löschen",
"Desktop App Settings": "Desktop App Einstellungen",
"Edit": "Bearbeiten",
"File": "Datei",
"Forward": "Nach vorne",
"Hard Reload": "Hard Reload",
"Help": "Hilfe",
"History": "Geschichte",
"Keyboard Shortcuts": "Tastatürkürzel",
"Log Out": "Ausloggen",
"Minimize": "Minimieren",
"Paste": "Einfügen",
"Paste and Match Style": "Einfügen und Anpassen von Stilen",
"Quit": "Verlassen",
"Redo": "Wiederholen",
"Reload": "Neu laden",
"Report an issue...": "Ein Problem melden...",
"Reset App Settings": "App-Einstellungen zurücksetzen",
"Select All": "Wählen Sie Alle",
"Show App Logs": "App-Logs anzeigen",
"Toggle DevTools for Active Tab": "Schalten Sie DevTools für Active Tab ein",
"Toggle DevTools for Zulip App": "Umschalt DevTools für Zulip App",
"Toggle Full Screen": "Vollbild umschalten",
"Toggle Sidebar": "Toggle Seitenleiste",
"Toggle Tray Icon": "Toggle Tray-Symbol",
"Undo": "Rückgängig machen",
"View": "Aussicht",
"Window": "Fenster",
"Zoom In": "Hineinzoomen",
"Zoom Out": "Rauszoomen",
"Zulip Help": "Zulip Hilfe"
}

39
app/translations/en.json Normal file
View File

@@ -0,0 +1,39 @@
{
"About Zulip": "About Zulip",
"Actual Size": "Actual Size",
"Back": "Back",
"Close": "Close",
"Copy": "Copy",
"Cut": "Cut",
"Delete": "Delete",
"Desktop App Settings": "Desktop App Settings",
"Edit": "Edit",
"File": "File",
"Forward": "Forward",
"Hard Reload": "Hard Reload",
"Help": "Help",
"History": "History",
"Keyboard Shortcuts": "Keyboard Shortcuts",
"Log Out": "Log Out",
"Minimize": "Minimize",
"Paste": "Paste",
"Paste and Match Style": "Paste and Match Style",
"Quit": "Quit",
"Redo": "Redo",
"Reload": "Reload",
"Report an issue...": "Report an issue...",
"Reset App Settings": "Reset App Settings",
"Select All": "Select All",
"Show App Logs": "Show App Logs",
"Toggle DevTools for Active Tab": "Toggle DevTools for Active Tab",
"Toggle DevTools for Zulip App": "Toggle DevTools for Zulip App",
"Toggle Full Screen": "Toggle Full Screen",
"Toggle Sidebar": "Toggle Sidebar",
"Toggle Tray Icon": "Toggle Tray Icon",
"Undo": "Undo",
"View": "View",
"Window": "Window",
"Zoom In": "Zoom In",
"Zoom Out": "Zoom Out",
"Zulip Help": "Zulip Help"
}

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